home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr44 / frasrc19.zip / HC.C < prev    next >
C/C++ Source or Header  |  1994-11-08  |  87KB  |  3,930 lines

  1.  
  2. /*
  3.  * hc.c
  4.  *
  5.  * Stand-alone FRACTINT help compiler.  Compile in the COMPACT memory model.
  6.  *
  7.  * See HC.DOC for source file syntax.
  8.  *
  9.  *
  10.  * Revision History:
  11.  *
  12.  *   02-26-91 EAN     Initial version.
  13.  *
  14.  *   03-21-91 EAN     Modified for automatic paragraph formatting.
  15.  *              Added several new commands:
  16.  *             Format[+/-]  Enable/disable paragraph formatting
  17.  *             Doc[+/-]     Enable/disable output to document.
  18.  *             Online[+/-]  Enable/disable output to online help.
  19.  *             Label=       Defines a label. Replaces ~(...)
  20.  *             FF          Forces a form-feed.  Replaces ~~
  21.  *             FormatExclude=val Exclude lines past val from
  22.  *                      formatting.  If before any topic sets
  23.  *                      global default, otherwise set local.
  24.  *             FormatExclude= Set to global default.
  25.  *             FormatExclude=n Disable exclusion. (global or local)
  26.  *             FormatExclude[+/-] Enable/disable format exclusion.
  27.  *             Center[+/-]  Enable/disable centering of text.
  28.  *             \ before nl  Forces the end of a paragraph
  29.  *              Support for commands embedded in text with new
  30.  *              ~(...) format.
  31.  *              Support for multiple commands on a line separated by
  32.  *              commas.
  33.  *              Support for implict links; explicit links must now
  34.  *              start with an equal sign.
  35.  *   04-03-91 EAN     Added "include" command (works like #include)
  36.  *   04-10-91 EAN     Added support for "data" topics.
  37.  *              Added Comment/EndComment commands for multi-line
  38.  *             comments.
  39.  *              Added CompressSpaces[+/-] command.
  40.  *              Added DocContents command for document printing.
  41.  *              Added BinInc command which includes a binary file
  42.  *             in a data topic.
  43.  *              Fixed tables to flow down instead of across the page.
  44.  *             Makes no allowances for page breaks within tables.
  45.  *   11-03-94 TIW     Increased buffer size.
  46.  *
  47.  */
  48.  
  49.  
  50. #define HC_C
  51.  
  52. #define INCLUDE_COMMON  /* tell helpcom.h to include common code */
  53.  
  54.  
  55. #include <stdio.h>
  56. #ifndef XFRACT
  57. #include <io.h>
  58. #include <stdarg.h>
  59. #else
  60. #include <varargs.h>
  61. #define strupr strlwr
  62. #endif
  63. #include <fcntl.h>
  64. #include <stdlib.h>
  65. #include <string.h>
  66. #include <ctype.h>
  67.  
  68. #ifdef __TURBOC__
  69. #   include <dir.h>
  70. #   define FNSPLIT fnsplit
  71. #else
  72. #   define MAXFILE _MAX_FNAME
  73. #   define MAXEXT  _MAX_EXT
  74. #   define FNSPLIT _splitpath
  75. #endif
  76.  
  77.  
  78. #include <assert.h>
  79. #include "helpcom.h"
  80.  
  81.  
  82. /*
  83.  * When defined, SHOW_ERROR_LINE will cause the line number in HC.C where
  84.  * errors/warnings/messages are generated to be displayed at the start of
  85.  * the line.
  86.  *
  87.  * Used when debugging HC.  Also useful for finding the line (in HC.C) that
  88.  * generated a error or warning.
  89.  */
  90.  
  91. #ifndef XFRACT
  92. #define SHOW_ERROR_LINE
  93. #endif
  94.  
  95.  
  96. #define DEFAULT_SRC_FNAME "help.src"
  97. #define DEFAULT_HLP_FNAME "fractint.hlp"
  98. #define DEFAULT_EXE_FNAME "fractint.exe"
  99. #define DEFAULT_DOC_FNAME "fractint.doc"
  100.  
  101. #define TEMP_FNAME      "HC.$$$"
  102. #define SWAP_FNAME      "HCSWAP.$$$"
  103.  
  104. #define MAX_ERRORS      (25)     /* stop after this many errors */
  105. #define MAX_WARNINGS      (25)     /* stop after this many warnings */
  106.                  /* 0 = never stop */
  107.  
  108. #define INDEX_LABEL      "HELP_INDEX"
  109. #define DOCCONTENTS_TITLE "DocContent"
  110.  
  111.  
  112.  
  113. /* #define BUFFER_SIZE   (24*1024) */
  114. #define BUFFER_SIZE   (30*1024)
  115.  
  116.  
  117. typedef struct
  118.    {
  119.    int        type;         /* 0 = name is topic title, 1 = name is label, */
  120.                  /*   2 = "special topic"; name is NULL and */
  121.                  /*   topic_num/topic_off is valid */
  122.    int        topic_num;         /* topic number to link to */
  123.    unsigned topic_off;         /* offset into topic to link to */
  124.    int        doc_page;         /* document page # to link to */
  125.    char    *name;         /* name of label or title of topic to link to */
  126.    char    *srcfile;         /* .SRC file link appears in */
  127.    int        srcline;         /* .SRC file line # link appears in */
  128.    } LINK;
  129.  
  130.  
  131. typedef struct
  132.    {
  133.    unsigned offset;     /* offset from start of topic text */
  134.    unsigned length;     /* length of page (in chars) */
  135.    int        margin;     /* if > 0 then page starts in_para and text */
  136.                 /* should be indented by this much */
  137.    } PAGE;
  138.  
  139.  
  140. /* values for TOPIC.flags */
  141.  
  142. #define TF_IN_DOC  (1)         /* 1 if topic is part of the printed document */
  143. #define TF_DATA    (2)         /* 1 if it is a "data" topic */
  144.  
  145.  
  146. typedef struct
  147.    {
  148.    unsigned  flags;         /* see #defines for TF_??? */
  149.    int         doc_page;         /* page number in document where topic starts */
  150.    unsigned  title_len;      /* length of title */
  151.    char     *title;         /* title for this topic */
  152.    int         num_page;         /* number of pages */
  153.    PAGE     *page;         /* list of pages */
  154.    unsigned  text_len;         /* lenth of topic text */
  155.    long      text;         /* topic text (all pages) */
  156.    long      offset;         /* offset to topic from start of file */
  157.    } TOPIC;
  158.  
  159.  
  160. typedef struct
  161.    {
  162.    char    *name;         /* its name */
  163.    int        topic_num;         /* topic number */
  164.    unsigned topic_off;         /* offset of label in the topic's text */
  165.    int        doc_page;
  166.    } LABEL;
  167.  
  168.  
  169. /* values for CONTENT.flags */
  170.  
  171. #define CF_NEW_PAGE  (1)     /* true if section starts on a new page */
  172.  
  173.  
  174. #define MAX_CONTENT_TOPIC (10)
  175.  
  176.  
  177. typedef struct
  178.    {
  179.    unsigned  flags;
  180.    char     *id;
  181.    char     *name;
  182.    int         doc_page;
  183.    unsigned  page_num_pos;
  184.    int         num_topic;
  185.    char      is_label[MAX_CONTENT_TOPIC];
  186.    char     *topic_name[MAX_CONTENT_TOPIC];
  187.    int         topic_num[MAX_CONTENT_TOPIC];
  188.    char     *srcfile;
  189.    int         srcline;
  190.    } CONTENT;
  191.  
  192.  
  193. struct help_sig_info
  194.    {
  195.    unsigned long sig;
  196.    int         version;
  197.    unsigned long base;
  198.    } ;
  199.  
  200.  
  201. int     num_topic      = 0;      /* topics */
  202. TOPIC   *topic;
  203.  
  204. int     num_label      = 0;      /* labels */
  205. LABEL   *label;
  206.  
  207. int     num_plabel      = 0;      /* private labels */
  208. LABEL   *plabel;
  209.  
  210. int     num_link      = 0;      /* all links */
  211. LINK    *a_link          = 0;
  212.  
  213. int     num_contents      = 0;      /* the table-of-contents */
  214. CONTENT *contents;
  215.  
  216. int     quiet_mode      = 0;      /* true if "/Q" option used */
  217.  
  218. int     max_pages      = 0;      /* max. pages in any topic */
  219. int     max_links      = 0;      /* max. links on any page */
  220. int     num_doc_pages      = 0;      /* total number of pages in document */
  221.  
  222. FILE    *srcfile;          /* .SRC file */
  223. int     srcline      = 0;      /* .SRC line number (used for errors) */
  224. int     srccol       = 0;      /* .SRC column. */
  225.  
  226. int     version      = -1;   /* help file version */
  227.  
  228. int     errors       = 0,      /* number of errors reported */
  229.      warnings      = 0;      /* number of warnings reported */
  230.  
  231. char     src_fname[81]      = "";   /* command-line .SRC filename */
  232. char     hdr_fname[81]      = "";   /* .H filename */
  233. char     hlp_fname[81]      = "";   /* .HLP filename */
  234. char    *src_cfname      = NULL; /* current .SRC filename */
  235.  
  236. int     format_exclude   = 0;      /* disable formatting at this col, 0 to */
  237.                   /*    never disable formatting */
  238. FILE    *swapfile;
  239. long     swappos;
  240.  
  241. char    *buffer;          /* alloc'ed as BUFFER_SIZE bytes */
  242. char    *curr;              /* current position in the buffer */
  243. char     cmd[128];          /* holds the current command */
  244. int     compress_spaces;
  245. int     xonline;
  246. int     xdoc;
  247.  
  248. #define  MAX_INCLUDE_STACK (5)      /* allow 5 nested includes */
  249.  
  250. struct
  251.    {
  252.    char *fname;
  253.    FILE *file;
  254.    int     line;
  255.    int     col;
  256.    } include_stack[MAX_INCLUDE_STACK];
  257. int include_stack_top = -1;
  258.  
  259.  
  260. #define CHK_BUFFER(off) { if ((unsigned)(curr+(off)) - (unsigned)buffer >= (BUFFER_SIZE-1024)) fatal(0,"Buffer overflowed -- Help topic too large."); }
  261.  
  262. #ifdef __WATCOMC__
  263. #define putw( x1, x2 )  fprintf( x2, "%c%c", x1&0xFF, x1>>8 );
  264. #endif
  265.  
  266. #ifdef XFRACT
  267. #define putw( x1, x2 )  fwrite( &(x1), 1, sizeof(int), x2);
  268. #endif
  269.  
  270. /*
  271.  * error/warning/message reporting functions.
  272.  */
  273.  
  274.  
  275. void report_errors(void)
  276.    {
  277.    printf("\n");
  278.    printf("Compiler Status:\n");
  279.    printf("%8d Error%c\n",     errors,   (errors==1)     ? ' ' : 's');
  280.    printf("%8d Warning%c\n",     warnings, (warnings==1) ? ' ' : 's');
  281.    }
  282.  
  283.  
  284. void print_msg(char *type, int lnum, char *format, va_list arg)
  285.    {
  286.    if (type != NULL)
  287.       {
  288.       printf("   %s", type);
  289.       if (lnum>0)
  290.      printf(" %s %d", src_cfname, lnum);
  291.       printf(": ");
  292.       }
  293.    vprintf(format, arg);
  294.    printf("\n");
  295.    }
  296.  
  297.  
  298. #ifndef XFRACT
  299. void fatal(int diff, char *format, ...)
  300. #else
  301. void fatal(va_alist)
  302.     va_dcl
  303. #endif
  304.    {
  305.    va_list arg;
  306.  
  307. #ifndef XFRACT
  308.    va_start(arg, format);
  309. #else
  310.    int diff;
  311.    char *format;
  312.    va_start(arg);
  313.    diff = va_arg(arg,int);
  314.    format = va_arg(arg,char *);
  315. #endif
  316.  
  317.    print_msg("Fatal", srcline-diff, format, arg);
  318.    va_end(arg);
  319.  
  320.    if ( errors || warnings )
  321.       report_errors();
  322.  
  323.    exit( errors + 1 );
  324.    }
  325.  
  326.  
  327. #ifndef XFRACT
  328. void error(int diff, char *format, ...)
  329. #else
  330. void error(va_alist)
  331.     va_dcl
  332. #endif
  333.    {
  334.    va_list arg;
  335.  
  336. #ifndef XFRACT
  337.    va_start(arg, format);
  338. #else
  339.    int diff;
  340.    char *format;
  341.    va_start(arg);
  342.    diff = va_arg(arg,int);
  343.    format = va_arg(arg,char *);
  344. #endif
  345.    print_msg("Error", srcline-diff, format, arg);
  346.    va_end(arg);
  347.  
  348.    if (++errors >= MAX_ERRORS && MAX_ERRORS > 0)
  349.       fatal(0,"Too many errors!");
  350.    }
  351.  
  352.  
  353. #ifndef XFRACT
  354. void warn(int diff, char *format, ...)
  355. #else
  356. void warn(va_alist)
  357.    va_dcl
  358. #endif
  359.    {
  360.    va_list arg;
  361. #ifndef XFRACT
  362.    va_start(arg, format);
  363. #else
  364.    int diff;
  365.    char *format;
  366.    va_start(arg);
  367.    diff = va_arg(arg, int);
  368.    format = va_arg(arg, char *);
  369. #endif
  370.    print_msg("Warning", srcline-diff, format, arg);
  371.    va_end(arg);
  372.  
  373.    if (++warnings >= MAX_WARNINGS && MAX_WARNINGS > 0)
  374.       fatal(0,"Too many warnings!");
  375.    }
  376.  
  377.  
  378. #ifndef XFRACT
  379. void notice(char *format, ...)
  380. #else
  381. void notice(va_alist)
  382.     va_dcl
  383. #endif
  384.    {
  385.    va_list arg;
  386. #ifndef XFRACT
  387.    va_start(arg, format);
  388. #else
  389.    char *format;
  390.  
  391.    va_start(arg);
  392.    format = va_arg(arg,char *);
  393. #endif
  394.    print_msg("Note", srcline, format, arg);
  395.    va_end(arg);
  396.    }
  397.  
  398.  
  399. #ifndef XFRACT
  400. void msg(char *format, ...)
  401. #else
  402. void msg(va_alist)
  403. va_dcl
  404. #endif
  405.    {
  406.    va_list arg;
  407. #ifdef XFRACT
  408.    char *format;
  409. #endif
  410.  
  411.    if (quiet_mode)
  412.       return;
  413. #ifndef XFRACT
  414.    va_start(arg, format);
  415. #else
  416.    va_start(arg);
  417.    format = va_arg(arg,char *);
  418. #endif
  419.    print_msg(NULL, 0, format, arg);
  420.    va_end(arg);
  421.    }
  422.  
  423.  
  424. #ifdef SHOW_ERROR_LINE
  425. #   define fatal  (printf("[%04d] ", __LINE__), fatal)
  426. #   define error  (printf("[%04d] ", __LINE__), error)
  427. #   define warn   (printf("[%04d] ", __LINE__), warn)
  428. #   define notice (printf("[%04d] ", __LINE__), notice)
  429. #   define msg      (printf((quiet_mode)?"":"[%04d] ", __LINE__), msg)
  430. #endif
  431.  
  432.  
  433. /*
  434.  * store-topic-text-to-disk stuff.
  435.  */
  436.  
  437.  
  438. void alloc_topic_text(TOPIC *t, unsigned size)
  439.    {
  440.    t->text_len = size;
  441.    t->text = swappos;
  442.    swappos += size;
  443.    fseek(swapfile, t->text, SEEK_SET);
  444.    fwrite(buffer, 1, t->text_len, swapfile);
  445.    }
  446.  
  447.  
  448. char *get_topic_text(TOPIC *t)
  449.    {
  450.    fseek(swapfile, t->text, SEEK_SET);
  451.    fread(buffer, 1, t->text_len, swapfile);
  452.    return (buffer);
  453.    }
  454.  
  455.  
  456. void release_topic_text(TOPIC *t, int save)
  457.    {
  458.    if ( save )
  459.       {
  460.       fseek(swapfile, t->text, SEEK_SET);
  461.       fwrite(buffer, 1, t->text_len, swapfile);
  462.       }
  463.    }
  464.  
  465.  
  466. /*
  467.  * memory-allocation functions.
  468.  */
  469.  
  470.  
  471. #define new(item)    (item *)newx(sizeof(item))
  472. #define delete(item) free(item)
  473.  
  474.  
  475. VOIDPTR newx(unsigned size)
  476.    {
  477.    VOIDPTR ptr;
  478.  
  479.    ptr = malloc(size);
  480.  
  481.    if (ptr == NULL)
  482.       fatal(0,"Out of memory!");
  483.  
  484.    return (ptr);
  485.    }
  486.  
  487.  
  488. VOIDPTR renewx(VOIDPTR ptr, unsigned size)
  489.    {
  490.    ptr = realloc(ptr, size);
  491.  
  492.    if (ptr == NULL)
  493.       fatal(0,"Out of memory!");
  494.  
  495.    return (ptr);
  496.    }
  497.  
  498.  
  499. char *dupstr(char *s, unsigned len)
  500.    {
  501.    char *ptr;
  502.  
  503.    if (len == 0)
  504.       len = strlen(s) + 1;
  505.  
  506.    ptr = newx(len);
  507.  
  508.    memcpy(ptr, s, len);
  509.  
  510.    return (ptr);
  511.    }
  512.  
  513.  
  514. #define LINK_ALLOC_SIZE (16)
  515.  
  516.  
  517. int add_link(LINK *l)
  518.    {
  519.    if (num_link == 0)
  520.       a_link = newx( sizeof(LINK)*LINK_ALLOC_SIZE );
  521.  
  522.    else if (num_link%LINK_ALLOC_SIZE == 0)
  523.       a_link = renewx(a_link, sizeof(LINK) * (num_link+LINK_ALLOC_SIZE) );
  524.  
  525.    a_link[num_link] = *l;
  526.  
  527.    return( num_link++ );
  528.    }
  529.  
  530.  
  531. #define PAGE_ALLOC_SIZE (4)
  532.  
  533.  
  534. int add_page(TOPIC *t, PAGE *p)
  535.    {
  536.    if (t->num_page == 0)
  537.       t->page = newx( sizeof(PAGE)*PAGE_ALLOC_SIZE );
  538.  
  539.    else if (t->num_page%PAGE_ALLOC_SIZE == 0)
  540.       t->page = renewx(t->page, sizeof(PAGE) * (t->num_page+PAGE_ALLOC_SIZE) );
  541.  
  542.    t->page[t->num_page] = *p;
  543.  
  544.    return ( t->num_page++ );
  545.    }
  546.  
  547.  
  548. #define TOPIC_ALLOC_SIZE (16)
  549.  
  550.  
  551. int add_topic(TOPIC *t)
  552.    {
  553.    if (num_topic == 0)
  554.       topic = newx( sizeof(TOPIC)*TOPIC_ALLOC_SIZE );
  555.  
  556.    else if (num_topic%TOPIC_ALLOC_SIZE == 0)
  557.       topic = renewx(topic, sizeof(TOPIC) * (num_topic+TOPIC_ALLOC_SIZE) );
  558.  
  559.    topic[num_topic] = *t;
  560.  
  561.    return ( num_topic++ );
  562.    }
  563.  
  564.  
  565. #define LABEL_ALLOC_SIZE (16)
  566.  
  567.  
  568. int add_label(LABEL *l)
  569.    {
  570.    if (l->name[0] == '@')    /* if it's a private label... */
  571.       {
  572.       if (num_plabel == 0)
  573.      plabel = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  574.  
  575.       else if (num_plabel%LABEL_ALLOC_SIZE == 0)
  576.      plabel = renewx(plabel, sizeof(LABEL) * (num_plabel+LABEL_ALLOC_SIZE) );
  577.  
  578.       plabel[num_plabel] = *l;
  579.  
  580.       return ( num_plabel++ );
  581.       }
  582.    else
  583.       {
  584.       if (num_label == 0)
  585.      label = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  586.  
  587.       else if (num_label%LABEL_ALLOC_SIZE == 0)
  588.      label = renewx(label, sizeof(LABEL) * (num_label+LABEL_ALLOC_SIZE) );
  589.  
  590.       label[num_label] = *l;
  591.  
  592.       return ( num_label++ );
  593.       }
  594.    }
  595.  
  596.  
  597. #define CONTENTS_ALLOC_SIZE (16)
  598.  
  599.  
  600. int add_content(CONTENT *c)
  601.    {
  602.    if (num_contents == 0)
  603.       contents = newx( sizeof(CONTENT)*CONTENTS_ALLOC_SIZE );
  604.  
  605.    else if (num_contents%CONTENTS_ALLOC_SIZE == 0)
  606.       contents = renewx(contents, sizeof(CONTENT) * (num_contents+CONTENTS_ALLOC_SIZE) );
  607.  
  608.    contents[num_contents] = *c;
  609.  
  610.    return ( num_contents++ );
  611.    }
  612.  
  613.  
  614. /*
  615.  * read_char() stuff
  616.  */
  617.  
  618.  
  619. #define READ_CHAR_BUFF_SIZE (32)
  620.  
  621.  
  622. int  read_char_buff[READ_CHAR_BUFF_SIZE];
  623. int  read_char_buff_pos = -1;
  624. int  read_char_sp       = 0;
  625.  
  626.  
  627. void unread_char(int ch)
  628.    /*
  629.     * Will not handle new-lines or tabs correctly!
  630.     */
  631.    {
  632.    if (read_char_buff_pos+1 >= READ_CHAR_BUFF_SIZE)
  633.       fatal(0,"Compiler Error -- Read char buffer overflow!");
  634.  
  635.    read_char_buff[++read_char_buff_pos] = ch;
  636.  
  637.    --srccol;
  638.    }
  639.  
  640.  
  641. void unread_string(char *s)
  642.    {
  643.    int p = strlen(s);
  644.  
  645.    while (p-- > 0)
  646.       unread_char(s[p]);
  647.    }
  648.  
  649.  
  650. int eos(void)     /* end-of-source ? */
  651.    {
  652.    return ( !((read_char_sp==0) && (read_char_buff_pos==0) && feof(srcfile)) );
  653.    }
  654.  
  655.  
  656. int _read_char(void)
  657.    {
  658.    int ch;
  659.  
  660.    if (srcline <= 0)
  661.       {
  662.       srcline = 1;
  663.       srccol = 0;
  664.       }
  665.  
  666.    if (read_char_buff_pos >= 0)
  667.       {
  668.       ++srccol;
  669.       return ( read_char_buff[read_char_buff_pos--] );
  670.       }
  671.  
  672.    if (read_char_sp > 0)
  673.       {
  674.       --read_char_sp;
  675.       return (' ');
  676.       }
  677.  
  678.    if ( feof(srcfile) )
  679.       return (-1);
  680.  
  681.    while (1)
  682.       {
  683.       ch = getc(srcfile);
  684.  
  685.       switch (ch)
  686.      {
  687.      case '\t':    /* expand a tab */
  688.         {
  689.         int diff = ( ( (srccol/8) + 1 ) * 8 ) - srccol;
  690.  
  691.         srccol += diff;
  692.         read_char_sp += diff;
  693.         break;
  694.         }
  695.  
  696.      case ' ':
  697.         ++srccol;
  698.         ++read_char_sp;
  699.         break;
  700.  
  701.      case '\n':
  702.         read_char_sp = 0;   /* delete spaces before a \n */
  703.         srccol = 0;
  704.         ++srcline;
  705.         return ('\n');
  706.  
  707.      case -1:            /* EOF */
  708.         if (read_char_sp > 0)
  709.            {
  710.            --read_char_sp;
  711.            return (' ');
  712.            }
  713.         return (-1);
  714.  
  715.      default:
  716.         if (read_char_sp > 0)
  717.            {
  718.            ungetc(ch, srcfile);
  719.            --read_char_sp;
  720.            return (' ');
  721.            }
  722.  
  723.         ++srccol;
  724.         return (ch);
  725.  
  726.      } /* switch */
  727.       }
  728.    }
  729.  
  730.  
  731. int read_char(void)
  732.    {
  733.    int ch;
  734.  
  735.    ch = _read_char();
  736.  
  737.    while (ch == ';' && srccol==1)    /* skip over comments */
  738.       {
  739.       ch = _read_char();
  740.  
  741.       while (ch!='\n' && ch!=-1 )
  742.      ch = _read_char();
  743.  
  744.       ch = _read_char();
  745.       }
  746.  
  747.    if (ch == '\\')   /* process an escape code */
  748.       {
  749.       ch = _read_char();
  750.  
  751.       if (ch >= '0' && ch <= '9')
  752.      {
  753.      char buff[4];
  754.      int  ctr;
  755.  
  756.      for (ctr=0; ; ctr++)
  757.         {
  758.         if ( ch<'0' || ch>'9' || ch==-1 || ctr>=3 )
  759.            {
  760.            unread_char(ch);
  761.            break;
  762.            }
  763.         buff[ctr] = ch;
  764.         ch = _read_char();
  765.         }
  766.      buff[ctr] = '\0';
  767.      ch = atoi(buff);
  768.      }
  769.  
  770. #ifdef XFRACT
  771.    /* Convert graphics arrows into keyboard chars */
  772.        if (ch>=24 && ch<=27) {
  773.        ch = "KJHL"[ch-24];
  774.        }
  775. #endif
  776.       ch |= 0x100;
  777.       }
  778.  
  779.    if ( (ch & 0xFF) == 0 )
  780.       {
  781.       error(0,"Null character (\'\\0\') not allowed!");
  782.       ch = 0x1FF; /* since we've had an error the file will not be written; */
  783.           /*   the value we return doesn't really matter */
  784.       }
  785.  
  786.    return(ch);
  787.    }
  788.  
  789.  
  790. /*
  791.  * misc. search functions.
  792.  */
  793.  
  794.  
  795. LABEL *find_label(char *name)
  796.    {
  797.    int      l;
  798.    LABEL *lp;
  799.  
  800.    if (*name == '@')
  801.       {
  802.       for (l=0, lp=plabel; l<num_plabel; l++, lp++)
  803.      if ( strcmp(name, lp->name) == 0 )
  804.         return (lp);
  805.       }
  806.    else
  807.       {
  808.       for (l=0, lp=label; l<num_label; l++, lp++)
  809.      if ( strcmp(name, lp->name) == 0 )
  810.         return (lp);
  811.       }
  812.  
  813.    return (NULL);
  814.    }
  815.  
  816.  
  817. int find_topic_title(char *title)
  818.    {
  819.    int t;
  820.    int len;
  821.  
  822.    while (*title == ' ')
  823.       ++title;
  824.  
  825.    len = strlen(title) - 1;
  826.    while ( title[len] == ' ' && len > 0 )
  827.       --len;
  828.  
  829.    ++len;
  830.  
  831.    if ( len > 2 && title[0] == '\"' && title[len-1] == '\"' )
  832.       {
  833.       ++title;
  834.       len -= 2;
  835.       }
  836.  
  837.    for (t=0; t<num_topic; t++)
  838.       if ( strlen(topic[t].title) == len &&
  839.        strnicmp(title, topic[t].title, len) == 0 )
  840.      return (t);
  841.  
  842.    return (-1);   /* not found */
  843.    }
  844.  
  845.  
  846. /*
  847.  * .SRC file parser stuff
  848.  */
  849.  
  850.  
  851. int validate_label_name(char *name)
  852.    {
  853.    if ( !isalpha(*name) && *name!='@' && *name!='_' )
  854.       return (0);  /* invalid */
  855.  
  856.    while (*(++name) != '\0')
  857.       if ( !isalpha(*name) && !isdigit(*name) && *name!='_' )
  858.      return(0);  /* invalid */
  859.  
  860.    return (1);  /* valid */
  861.    }
  862.  
  863.  
  864. char *read_until(char *buff, int len, char *stop_chars)
  865.    {
  866.    int ch;
  867.  
  868.    while ( --len > 0 )
  869.       {
  870.       ch = read_char();
  871.  
  872.       if ( ch == -1 )
  873.      {
  874.      *buff++ = '\0';
  875.      break;
  876.      }
  877.  
  878.       if ( (ch&0xFF) <= MAX_CMD )
  879.      *buff++ = CMD_LITERAL;
  880.  
  881.       *buff++ = ch;
  882.  
  883.       if ( (ch&0x100)==0 && strchr(stop_chars, ch) != NULL )
  884.      break;
  885.       }
  886.  
  887.    return ( buff-1 );
  888.    }
  889.  
  890.  
  891. void skip_over(char *skip)
  892.    {
  893.    int ch;
  894.  
  895.    while (1)
  896.       {
  897.       ch = read_char();
  898.  
  899.       if ( ch == -1 )
  900.      break;
  901.  
  902.       else if ( (ch&0x100) == 0 && strchr(skip, ch) == NULL )
  903.      {
  904.      unread_char(ch);
  905.      break;
  906.      }
  907.       }
  908.    }
  909.  
  910.  
  911. char *pchar(int ch)
  912.    {
  913.    static char buff[16];
  914.  
  915.    if ( ch >= 0x20 && ch <= 0x7E )
  916.       sprintf(buff, "\'%c\'", ch);
  917.    else
  918.       sprintf(buff, "\'\\x%02X\'", ch&0xFF);
  919.  
  920.    return (buff);
  921.    }
  922.  
  923.  
  924. void put_spaces(int how_many)
  925.    {
  926.    if (how_many > 2 && compress_spaces)
  927.       {
  928.       if (how_many > 255)
  929.      {
  930.      error(0,"Too many spaces (over 255).");
  931.      how_many = 255;
  932.      }
  933.  
  934.       *curr++ = CMD_SPACE;
  935.       *curr++ = (BYTE)how_many;
  936.       }
  937.    else
  938.       {
  939.       while (how_many-- > 0)
  940.      *curr++ = ' ';
  941.       }
  942.    }
  943.  
  944.  
  945. int get_next_item(void)   /* used by parse_contents() */
  946.    {
  947.    int     last;
  948.    char *ptr;
  949.  
  950.    skip_over(" \t\n");
  951.    ptr = read_until(cmd, 128, ",}");
  952.    last = (*ptr == '}');
  953.    --ptr;
  954.    while ( ptr >= cmd && strchr(" \t\n",*ptr) )   /* strip trailing spaces */
  955.       --ptr;
  956.    *(++ptr) = '\0';
  957.  
  958.    return (last);
  959.    }
  960.  
  961.  
  962. void process_contents(void)
  963.    {
  964.    CONTENT c;
  965.    char   *ptr;
  966.    int       indent;
  967.    int       ch;
  968.    TOPIC   t;
  969.  
  970.    t.flags     = 0;
  971.    t.title_len = strlen(DOCCONTENTS_TITLE)+1;
  972.    t.title     = dupstr(DOCCONTENTS_TITLE, t.title_len);
  973.    t.doc_page  = -1;
  974.    t.num_page  = 0;
  975.  
  976.    curr = buffer;
  977.  
  978.    c.flags = 0;
  979.    c.id = dupstr("",1);
  980.    c.name = dupstr("",1);
  981.    c.doc_page = -1;
  982.    c.page_num_pos = 0;
  983.    c.num_topic = 1;
  984.    c.is_label[0] = 0;
  985.    c.topic_name[0] = dupstr(DOCCONTENTS_TITLE,0);
  986.    c.srcline = -1;
  987.    add_content(&c);
  988.  
  989.    while (1)
  990.       {
  991.       ch = read_char();
  992.  
  993.       if (ch == '{')   /* process a CONTENT entry */
  994.      {
  995.      int last;
  996.  
  997.      c.flags = 0;
  998.      c.num_topic = 0;
  999.      c.doc_page = -1;
  1000.      c.srcfile = src_cfname;
  1001.      c.srcline = srcline;
  1002.  
  1003.      if ( get_next_item() )
  1004.         {
  1005.         error(0,"Unexpected end of DocContent entry.");
  1006.         continue;
  1007.         }
  1008.      c.id = dupstr(cmd,0);
  1009.  
  1010.      if ( get_next_item() )
  1011.         {
  1012.         error(0,"Unexpected end of DocContent entry.");
  1013.         continue;
  1014.         }
  1015.      indent = atoi(cmd);
  1016.  
  1017.      last = get_next_item();
  1018.  
  1019.      if ( cmd[0] == '\"' )
  1020.         {
  1021.         ptr = cmd+1;
  1022.         if (ptr[strlen(ptr)-1] == '\"')
  1023.            ptr[strlen(ptr)-1] = '\0';
  1024.         else
  1025.            warn(0,"Missing ending quote.");
  1026.  
  1027.         c.is_label[c.num_topic] = 0;
  1028.         c.topic_name[c.num_topic] = dupstr(ptr,0);
  1029.         ++c.num_topic;
  1030.         c.name = dupstr(ptr,0);
  1031.         }
  1032.      else
  1033.         c.name = dupstr(cmd,0);
  1034.  
  1035.      /* now, make the entry in the buffer */
  1036.  
  1037.      sprintf(curr, "%-5s %*.0s%s", c.id, indent*2, "", c.name);
  1038.      ptr = curr + strlen(curr);
  1039.      while ( (ptr-curr) < PAGE_WIDTH-10 )
  1040.         *ptr++ = '.';
  1041.      c.page_num_pos = (unsigned) ( (ptr-3) - buffer );
  1042.      curr = ptr;
  1043.  
  1044.      while (!last)
  1045.         {
  1046.         last = get_next_item();
  1047.  
  1048.         if ( stricmp(cmd, "FF") == 0 )
  1049.            {
  1050.            if ( c.flags & CF_NEW_PAGE )
  1051.           warn(0,"FF already present in this entry.");
  1052.            c.flags |= CF_NEW_PAGE;
  1053.            continue;
  1054.            }
  1055.  
  1056.         if (cmd[0] == '\"')
  1057.            {
  1058.            ptr = cmd+1;
  1059.            if (ptr[strlen(ptr)-1] == '\"')
  1060.           ptr[strlen(ptr)-1] = '\0';
  1061.            else
  1062.           warn(0,"Missing ending quote.");
  1063.  
  1064.            c.is_label[c.num_topic] = 0;
  1065.            c.topic_name[c.num_topic] = dupstr(ptr,0);
  1066.            }
  1067.         else
  1068.            {
  1069.            c.is_label[c.num_topic] = 1;
  1070.            c.topic_name[c.num_topic] = dupstr(cmd,0);
  1071.            }
  1072.  
  1073.         if ( ++c.num_topic >= MAX_CONTENT_TOPIC )
  1074.            {
  1075.            error(0,"Too many topics in DocContent entry.");
  1076.            break;
  1077.            }
  1078.         }
  1079.  
  1080.      add_content(&c);
  1081.      }
  1082.  
  1083.       else if (ch == '~')   /* end at any command */
  1084.      {
  1085.      unread_char(ch);
  1086.      break;
  1087.      }
  1088.  
  1089.       else
  1090.      *curr++ = ch;
  1091.  
  1092.       CHK_BUFFER(0);
  1093.       }
  1094.  
  1095.    alloc_topic_text(&t, (unsigned) (curr - buffer) );
  1096.    add_topic(&t);
  1097.    }
  1098.  
  1099.  
  1100. int parse_link(void)   /* returns length of link or 0 on error */
  1101.    {
  1102.    char *ptr;
  1103.    char *end;
  1104.    int     bad = 0;
  1105.    int     len;
  1106.    LINK  l;
  1107.    int     lnum;
  1108.    int     err_off;
  1109.  
  1110.    l.srcfile  = src_cfname;
  1111.    l.srcline  = srcline;
  1112.    l.doc_page = -1;
  1113.  
  1114.    end = read_until(cmd, 128, "}\n");   /* get the entire hot-link */
  1115.  
  1116.    if (*end == '\0')
  1117.       {
  1118.       error(0,"Unexpected EOF in hot-link.");
  1119.       return (0);
  1120.       }
  1121.  
  1122.    if (*end == '\n')
  1123.       {
  1124.       err_off = 1;
  1125.       warn(1,"Hot-link has no closing curly-brace (\'}\').");
  1126.       }
  1127.    else
  1128.       err_off = 0;
  1129.  
  1130.    *end = '\0';
  1131.  
  1132.    if (cmd[0] == '=')   /* it's an "explicit" link to a label or "special" */
  1133.       {
  1134.       ptr = strchr(cmd, ' ');
  1135.  
  1136.       if (ptr == NULL)
  1137.      ptr = end;
  1138.       else
  1139.      *ptr++ = '\0';
  1140.  
  1141.       len = (int) (end - ptr);
  1142.  
  1143.       if ( cmd[1] == '-' )
  1144.      {
  1145.      l.type      = 2;       /* type 2 = "special" */
  1146.      l.topic_num = atoi(cmd+1);
  1147.      l.topic_off = 0;
  1148.      l.name      = NULL;
  1149.      }
  1150.       else
  1151.      {
  1152.      l.type = 1;           /* type 1 = to a label */
  1153.      if (strlen(cmd) > 32)
  1154.         warn(err_off, "Label is long.");
  1155.      if (cmd[1] == '\0')
  1156.         {
  1157.         error(err_off, "Explicit hot-link has no Label.");
  1158.         bad = 1;
  1159.         }
  1160.      else
  1161.         l.name = dupstr(cmd+1,0);
  1162.      }
  1163.       if (len == 0)
  1164.      warn(err_off, "Explicit hot-link has no title.");
  1165.       }
  1166.    else
  1167.       {
  1168.       ptr = cmd;
  1169.       l.type = 0;   /* type 0 = topic title */
  1170.       len = (int) (end - ptr);
  1171.       if (len == 0)
  1172.      {
  1173.      error(err_off, "Implicit hot-link has no title.");
  1174.      bad = 1;
  1175.      }
  1176.       l.name = dupstr(ptr,len+1);
  1177.       l.name[len] = '\0';
  1178.       }
  1179.  
  1180.    if ( !bad )
  1181.       {
  1182.       CHK_BUFFER(1+3*sizeof(int)+len+1)
  1183.       lnum = add_link(&l);
  1184.       *curr++ = CMD_LINK;
  1185.       setint(curr,lnum);
  1186.       curr += 3*sizeof(int);
  1187.       memcpy(curr, ptr, len);
  1188.       curr += len;
  1189.       *curr++ = CMD_LINK;
  1190.       return (len);
  1191.       }
  1192.    else
  1193.       return (0);
  1194.    }
  1195.  
  1196.  
  1197. #define MAX_TABLE_SIZE (100)
  1198.  
  1199.  
  1200. int create_table(void)
  1201.    {
  1202.    char  *ptr;
  1203.    int      width;
  1204.    int      cols;
  1205.    int      start_off;
  1206.    int      first_link;
  1207.    int      rows;
  1208.    int      r, c;
  1209.    int      ch;
  1210.    int      done;
  1211.    int      len;
  1212.    int      lnum;
  1213.    int      count;
  1214.    char  *title[MAX_TABLE_SIZE];
  1215.    char  *table_start;
  1216.  
  1217.    ptr = strchr(cmd, '=');
  1218.  
  1219.    if (ptr == NULL)
  1220.       return (0);   /* should never happen! */
  1221.  
  1222.    ptr++;
  1223.  
  1224.    len = sscanf(ptr, " %d %d %d", &width, &cols, &start_off);
  1225.  
  1226.    if (len < 3)
  1227.       {
  1228.       error(1,"Too few arguments to Table.");
  1229.       return (0);
  1230.       }
  1231.  
  1232.    if (width<=0 || width > 78 || cols<=0 || start_off<0 || start_off > 78)
  1233.       {
  1234.       error(1,"Argument out of range.");
  1235.       return (0);
  1236.       }
  1237.  
  1238.    done = 0;
  1239.  
  1240.    first_link = num_link;
  1241.    table_start = curr;
  1242.    count = 0;
  1243.  
  1244.    /* first, read all the links in the table */
  1245.  
  1246.    do
  1247.       {
  1248.  
  1249.       do
  1250.      ch = read_char();
  1251.       while ( ch=='\n' || ch == ' ' );
  1252.  
  1253.       if (done)
  1254.      break;
  1255.  
  1256.       switch (ch)
  1257.      {
  1258.      case -1:
  1259.         error(0,"Unexpected EOF in a Table.");
  1260.         return(0);
  1261.  
  1262.      case '{':
  1263.         if (count >= MAX_TABLE_SIZE)
  1264.            fatal(0,"Table is too large.");
  1265.         len = parse_link();
  1266.         curr = table_start;   /* reset to the start... */
  1267.             title[count] = dupstr(curr+3*sizeof(int)+1, len+1);
  1268.         if (len >= width)
  1269.            {
  1270.            warn(1,"Link is too long; truncating.");
  1271.            len = width-1;
  1272.            }
  1273.         title[count][len] = '\0';
  1274.         ++count;
  1275.         break;
  1276.  
  1277.      case '~':
  1278.         {
  1279.         int imbedded;
  1280.  
  1281.         ch = read_char();
  1282.  
  1283.         if (ch=='(')
  1284.            imbedded = 1;
  1285.         else
  1286.            {
  1287.            imbedded = 0;
  1288.            unread_char(ch);
  1289.            }
  1290.  
  1291.         ptr = read_until(cmd, 128, ")\n,");
  1292.  
  1293.         ch = *ptr;
  1294.         *ptr = '\0';
  1295.  
  1296.         if  ( stricmp(cmd, "EndTable") == 0 )
  1297.            done = 1;
  1298.         else
  1299.            {
  1300.            error(1,"Unexpected command in table \"%s\"", cmd);
  1301.            warn(1,"Command will be ignored.");
  1302.            }
  1303.  
  1304.         if (ch == ',')
  1305.            {
  1306.            if (imbedded)
  1307.           unread_char('(');
  1308.            unread_char('~');
  1309.            }
  1310.         }
  1311.         break;
  1312.  
  1313.      default:
  1314.         error(0,"Unexpected character %s.", pchar(ch));
  1315.         break;
  1316.      }
  1317.       }
  1318.    while (!done);
  1319.  
  1320.    /* now, put all the links into the buffer... */
  1321.  
  1322.    rows = 1 + ( count / cols );
  1323.  
  1324.    for (r=0; r<rows; r++)
  1325.       {
  1326.       put_spaces(start_off);
  1327.       for (c=0; c<cols; c++)
  1328.      {
  1329.      lnum = c*rows + r;
  1330.  
  1331.      if ( first_link+lnum >= num_link )
  1332.         break;
  1333.  
  1334.      len = strlen(title[lnum]);
  1335.      *curr++ = CMD_LINK;
  1336.          setint(curr,first_link+lnum);
  1337.          curr += 3*sizeof(int);
  1338.      memcpy(curr, title[lnum], len);
  1339.      curr += len;
  1340.      *curr++ = CMD_LINK;
  1341.  
  1342.      delete(title[lnum]);
  1343.  
  1344.      if ( c < cols-1 )
  1345.         put_spaces( width-len );
  1346.      }
  1347.       *curr++ = '\n';
  1348.       }
  1349.  
  1350.    return (1);
  1351.    }
  1352.  
  1353.  
  1354. void process_comment(void)
  1355.    {
  1356.    int ch;
  1357.  
  1358.    while ( 1 )
  1359.       {
  1360.       ch = read_char();
  1361.  
  1362.       if (ch == '~')
  1363.      {
  1364.      int   imbedded;
  1365.      char *ptr;
  1366.  
  1367.      ch = read_char();
  1368.  
  1369.      if (ch=='(')
  1370.         imbedded = 1;
  1371.      else
  1372.         {
  1373.         imbedded = 0;
  1374.         unread_char(ch);
  1375.         }
  1376.  
  1377.      ptr = read_until(cmd, 128, ")\n,");
  1378.  
  1379.      ch = *ptr;
  1380.      *ptr = '\0';
  1381.  
  1382.      if  ( stricmp(cmd, "EndComment") == 0 )
  1383.         {
  1384.         if (ch == ',')
  1385.            {
  1386.            if (imbedded)
  1387.           unread_char('(');
  1388.            unread_char('~');
  1389.            }
  1390.         break;
  1391.         }
  1392.      }
  1393.  
  1394.       else if ( ch == -1 )
  1395.      {
  1396.      error(0,"Unexpected EOF in Comment");
  1397.      break;
  1398.      }
  1399.       }
  1400.    }
  1401.  
  1402.  
  1403. void process_bininc(void)
  1404.    {
  1405.    int  handle;
  1406.    long len;
  1407.  
  1408.    if ( (handle=open(cmd+7, O_RDONLY|O_BINARY)) == -1 )
  1409.       {
  1410.       error(0,"Unable to open \"%s\"", cmd+7);
  1411.       return ;
  1412.       }
  1413.  
  1414.    len = filelength(handle);
  1415.  
  1416.    if ( len >= BUFFER_SIZE )
  1417.       {
  1418.       error(0,"File \"%s\" is too large to BinInc (%dK).", cmd+7, (int)(len>>10));
  1419.       close(handle);
  1420.       return ;
  1421.       }
  1422.  
  1423.    /*
  1424.     * Since we know len is less than BUFFER_SIZE (and therefore less then
  1425.     * 64K) we can treat it as an unsigned.
  1426.     */
  1427.  
  1428.    CHK_BUFFER((unsigned)len);
  1429.  
  1430.    read(handle, curr, (unsigned)len);
  1431.  
  1432.    curr += (unsigned)len;
  1433.  
  1434.    close(handle);
  1435.    }
  1436.  
  1437.  
  1438. void start_topic(TOPIC *t, char *title, int title_len)
  1439.    {
  1440.    t->flags = 0;
  1441.    t->title_len = title_len;
  1442.    t->title = dupstr(title, title_len+1);
  1443.    t->title[title_len] = '\0';
  1444.    t->doc_page = -1;
  1445.    t->num_page = 0;
  1446.    curr = buffer;
  1447.    }
  1448.  
  1449.  
  1450. void end_topic(TOPIC *t)
  1451.    {
  1452.    alloc_topic_text(t, (unsigned) (curr - buffer) );
  1453.    add_topic(t);
  1454.    }
  1455.  
  1456.  
  1457. int end_of_sentence(char *ptr)  /* true if ptr is at the end of a sentence */
  1458.    {
  1459.    if ( *ptr == ')')
  1460.       --ptr;
  1461.  
  1462.    if ( *ptr == '\"')
  1463.       --ptr;
  1464.  
  1465.    return ( *ptr=='.' || *ptr=='?' || *ptr=='!' );
  1466.    }
  1467.  
  1468.  
  1469. void add_blank_for_split(void)     /* add space at curr for merging two lines */
  1470.    {
  1471.    if ( !is_hyphen(curr-1) )   /* no spaces if it's a hyphen */
  1472.       {
  1473.       if ( end_of_sentence(curr-1) )
  1474.      *curr++ = ' ';  /* two spaces at end of a sentence */
  1475.       *curr++ = ' ';
  1476.       }
  1477.    }
  1478.  
  1479.  
  1480. void put_a_char(int ch, TOPIC *t)
  1481.    {
  1482.    if (ch == '{' && !(t->flags & TF_DATA) )   /* is it a hot-link? */
  1483.       parse_link();
  1484.    else
  1485.       {
  1486.       if ( (ch&0xFF) <= MAX_CMD)
  1487.      *curr++ = CMD_LITERAL;
  1488.       *curr++ = ch;
  1489.       }
  1490.    }
  1491.  
  1492.  
  1493. enum STATES   /* states for FSM's */
  1494.    {
  1495.    S_Start,            /* initial state, between paragraphs       */
  1496.    S_StartFirstLine,        /* spaces at start of first line           */
  1497.    S_FirstLine,         /* text on the first line               */
  1498.    S_FirstLineSpaces,        /* spaces on the first line            */
  1499.    S_StartSecondLine,        /* spaces at start of second line           */
  1500.    S_Line,            /* text on lines after the first           */
  1501.    S_LineSpaces,        /* spaces on lines after the first           */
  1502.    S_StartLine,         /* spaces at start of lines after second       */
  1503.    S_FormatDisabled,        /* format automatically disabled for this line */
  1504.    S_FormatDisabledSpaces,  /* spaces in line which format is disabled       */
  1505.    S_Spaces
  1506.    } ;
  1507.  
  1508.  
  1509. void check_command_length(int eoff, int len)
  1510.    {
  1511.    if (strlen(cmd) != len)
  1512.       error(eoff, "Invalid text after a command \"%s\"", cmd+len);
  1513.    }
  1514.  
  1515.  
  1516. void read_src(char *fname)
  1517.    {
  1518.    int      ch;
  1519.    char  *ptr;
  1520.    TOPIC  t;
  1521.    LABEL  lbl;
  1522.    char  *margin_pos = NULL;
  1523.    int      in_topic   = 0,
  1524.       formatting = 1,
  1525.       state      = S_Start,
  1526.       num_spaces = 0,
  1527.       margin     = 0,
  1528.       in_para    = 0,
  1529.       centering  = 0,
  1530.       lformat_exclude = format_exclude,
  1531.       again;
  1532.  
  1533.    xonline = xdoc = 0;
  1534.  
  1535.    src_cfname = fname;
  1536.  
  1537.    if ( (srcfile = fopen(fname, "rt")) == NULL )
  1538.       fatal(0,"Unable to open \"%s\"", fname);
  1539.  
  1540.    msg("Compiling: %s", fname);
  1541.  
  1542.    in_topic = 0;
  1543.  
  1544.    curr = buffer;
  1545.  
  1546.    while ( 1 )
  1547.       {
  1548.  
  1549.       ch = read_char();
  1550.  
  1551.       if ( ch == -1 )   /* EOF? */
  1552.      {
  1553.      if ( include_stack_top >= 0)
  1554.         {
  1555.         fclose(srcfile);
  1556.         src_cfname = include_stack[include_stack_top].fname;
  1557.         srcfile = include_stack[include_stack_top].file;
  1558.         srcline = include_stack[include_stack_top].line;
  1559.         srccol  = include_stack[include_stack_top].col;
  1560.         --include_stack_top;
  1561.         continue;
  1562.         }
  1563.      else
  1564.         {
  1565.         if (in_topic)  /* if we're in a topic, finish it */
  1566.            end_topic(&t);
  1567.         if (num_topic == 0)
  1568.            warn(0,".SRC file has no topics.");
  1569.         break;
  1570.         }
  1571.      }
  1572.  
  1573.       if (ch == '~')   /* is is a command? */
  1574.      {
  1575.      int imbedded;
  1576.      int eoff;
  1577.      int done;
  1578.  
  1579.      ch = read_char();
  1580.      if (ch == '(')
  1581.         {
  1582.         imbedded = 1;
  1583.         eoff = 0;
  1584.         }
  1585.      else
  1586.         {
  1587.         imbedded = 0;
  1588.         eoff=0;
  1589.         unread_char(ch);
  1590.         }
  1591.  
  1592.      done = 0;
  1593.  
  1594.      while ( !done )
  1595.         {
  1596.         do
  1597.            ch = read_char();
  1598.         while (ch == ' ');
  1599.         unread_char(ch);
  1600.  
  1601.         if (imbedded)
  1602.            ptr = read_until(cmd, 128, ")\n,");
  1603.         else
  1604.            ptr = read_until(cmd, 128, "\n,");
  1605.  
  1606.         done = 1;
  1607.  
  1608.         if ( *ptr == '\0' )
  1609.            {
  1610.            error(0,"Unexpected EOF in command.");
  1611.            break;
  1612.            }
  1613.  
  1614.         if (*ptr == '\n')
  1615.            ++eoff;
  1616.  
  1617.         if ( imbedded && *ptr == '\n' )
  1618.            error(eoff,"Imbedded command has no closing parend (\')\')");
  1619.  
  1620.         done = (*ptr != ',');   /* we done if it's not a comma */
  1621.  
  1622.         if ( *ptr != '\n' && *ptr != ')' && *ptr != ',' )
  1623.            {
  1624.            error(0,"Command line too long.");
  1625.            break;
  1626.            }
  1627.  
  1628.         *ptr = '\0';
  1629.  
  1630.  
  1631.         /* commands allowed anytime... */
  1632.  
  1633.         if ( strnicmp(cmd, "Topic=", 6) == 0 )
  1634.            {
  1635.            if (in_topic)  /* if we're in a topic, finish it */
  1636.           end_topic(&t);
  1637.            else
  1638.           in_topic = 1;
  1639.  
  1640.            if (cmd[6] == '\0')
  1641.           warn(eoff,"Topic has no title.");
  1642.  
  1643.            else if (strlen(cmd+6) > 70)
  1644.           error(eoff,"Topic title is too long.");
  1645.  
  1646.            else if (strlen(cmd+6) > 60)
  1647.           warn(eoff,"Topic title is long.");
  1648.  
  1649.            if ( find_topic_title(cmd+6) != -1 )
  1650.           error(eoff,"Topic title already exists.");
  1651.  
  1652.            start_topic(&t, cmd+6, (unsigned)(ptr-(cmd+6)));
  1653.            formatting = 1;
  1654.            centering = 0;
  1655.            state = S_Start;
  1656.            in_para = 0;
  1657.            num_spaces = 0;
  1658.            xonline = xdoc = 0;
  1659.            lformat_exclude = format_exclude;
  1660.            compress_spaces = 1;
  1661.            continue;
  1662.            }
  1663.  
  1664.         else if ( strnicmp(cmd, "Data=", 5) == 0 )
  1665.            {
  1666.            if (in_topic)  /* if we're in a topic, finish it */
  1667.           end_topic(&t);
  1668.            else
  1669.           in_topic = 1;
  1670.  
  1671.            if (cmd[5] == '\0')
  1672.           warn(eoff,"Data topic has no label.");
  1673.  
  1674.            if ( !validate_label_name(cmd+5) )
  1675.           {
  1676.           error(eoff,"Label \"%s\" contains illegal characters.", cmd+5);
  1677.           continue;
  1678.           }
  1679.  
  1680.            if ( find_label(cmd+5) != NULL )
  1681.           {
  1682.           error(eoff,"Label \"%s\" already exists", cmd+5);
  1683.           continue;
  1684.           }
  1685.  
  1686.            if ( cmd[5] == '@' )
  1687.           warn(eoff, "Data topic has a local label.");
  1688.  
  1689.            start_topic(&t, "", 0);
  1690.            t.flags |= TF_DATA;
  1691.  
  1692.            if (strlen(cmd+5) > 32)
  1693.           warn(eoff,"Label name is long.");
  1694.  
  1695.            lbl.name      = dupstr(cmd+5, 0);
  1696.            lbl.topic_num = num_topic;
  1697.            lbl.topic_off = 0;
  1698.            lbl.doc_page  = -1;
  1699.            add_label(&lbl);
  1700.  
  1701.            formatting = 0;
  1702.            centering = 0;
  1703.            state = S_Start;
  1704.            in_para = 0;
  1705.            num_spaces = 0;
  1706.            xonline = xdoc = 0;
  1707.            lformat_exclude = format_exclude;
  1708.            compress_spaces = 0;
  1709.            continue;
  1710.            }
  1711.  
  1712.         else if ( strnicmp(cmd, "DocContents", 11) == 0 )
  1713.            {
  1714.            check_command_length(eoff, 11);
  1715.            if (in_topic)  /* if we're in a topic, finish it */
  1716.           end_topic(&t);
  1717.            if (!done)
  1718.           {
  1719.           if (imbedded)
  1720.              unread_char('(');
  1721.           unread_char('~');
  1722.           done = 1;
  1723.           }
  1724.            compress_spaces = 1;
  1725.            process_contents();
  1726.            in_topic = 0;
  1727.            continue;
  1728.            }
  1729.  
  1730.         else if ( stricmp(cmd, "Comment") == 0 )
  1731.            {
  1732.            process_comment();
  1733.            continue;
  1734.            }
  1735.  
  1736.         else if ( strnicmp(cmd, "FormatExclude", 13) == 0 )
  1737.            {
  1738.            if (cmd[13] == '-')
  1739.           {
  1740.           check_command_length(eoff, 14);
  1741.           if ( in_topic )
  1742.              {
  1743.              if (lformat_exclude > 0)
  1744.                 lformat_exclude = -lformat_exclude;
  1745.              else
  1746.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1747.              }
  1748.           else
  1749.              {
  1750.              if (format_exclude > 0)
  1751.                 format_exclude = -format_exclude;
  1752.              else
  1753.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1754.              }
  1755.           }
  1756.            else if (cmd[13] == '+')
  1757.           {
  1758.           check_command_length(eoff,14);
  1759.           if ( in_topic )
  1760.              {
  1761.              if (lformat_exclude < 0)
  1762.                 lformat_exclude = -lformat_exclude;
  1763.              else
  1764.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1765.              }
  1766.           else
  1767.              {
  1768.              if (format_exclude < 0)
  1769.                 format_exclude = -format_exclude;
  1770.              else
  1771.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1772.              }
  1773.           }
  1774.            else if (cmd[13] == '=')
  1775.           {
  1776.           if (cmd[14] == 'n' || cmd[14] == 'N')
  1777.              {
  1778.              check_command_length(eoff,15);
  1779.              if (in_topic)
  1780.                 lformat_exclude = 0;
  1781.              else
  1782.                format_exclude = 0;
  1783.              }
  1784.           else if (cmd[14] == '\0')
  1785.              lformat_exclude = format_exclude;
  1786.           else
  1787.              {
  1788.              int n = ( ( (in_topic) ? lformat_exclude : format_exclude) < 0 ) ? -1 : 1;
  1789.  
  1790.              lformat_exclude = atoi(cmd+14);
  1791.  
  1792.              if ( lformat_exclude <= 0 )
  1793.                 {
  1794.                 error(eoff,"Invalid argument to FormatExclude=");
  1795.                 lformat_exclude = 0;
  1796.                 }
  1797.  
  1798.              lformat_exclude *= n;
  1799.  
  1800.              if ( !in_topic )
  1801.                 format_exclude = lformat_exclude;
  1802.              }
  1803.           }
  1804.            else
  1805.           error(eoff,"Invalid format for FormatExclude");
  1806.  
  1807.            continue;
  1808.            }
  1809.  
  1810.         else if ( strnicmp(cmd, "Include ", 8) == 0 )
  1811.            {
  1812.            if (include_stack_top >= MAX_INCLUDE_STACK-1)
  1813.           error(eoff, "Too many nested Includes.");
  1814.            else
  1815.           {
  1816.           ++include_stack_top;
  1817.           include_stack[include_stack_top].fname = src_cfname;
  1818.           include_stack[include_stack_top].file = srcfile;
  1819.           include_stack[include_stack_top].line = srcline;
  1820.           include_stack[include_stack_top].col  = srccol;
  1821.           strupr(cmd+8);
  1822.           if ( (srcfile = fopen(cmd+8, "rt")) == NULL )
  1823.              {
  1824.              error(eoff, "Unable to open \"%s\"", cmd+8);
  1825.              srcfile = include_stack[include_stack_top--].file;
  1826.              }
  1827.           src_cfname = dupstr(cmd+8,0);  /* never deallocate! */
  1828.           srcline = 1;
  1829.           srccol = 0;
  1830.           }
  1831.  
  1832.            continue;
  1833.            }
  1834.  
  1835.  
  1836.         /* commands allowed only before all topics... */
  1837.  
  1838.         if ( !in_topic )
  1839.            {
  1840.            if ( strnicmp(cmd, "HdrFile=", 8) == 0 )
  1841.           {
  1842.           if (hdr_fname[0] != '\0')
  1843.              warn(eoff,"Header Filename has already been defined.");
  1844.           strcpy(hdr_fname, cmd+8);
  1845.           strupr(hdr_fname);
  1846.           }
  1847.  
  1848.            else if ( strnicmp(cmd, "HlpFile=", 8) == 0 )
  1849.           {
  1850.           if (hlp_fname[0] != '\0')
  1851.              warn(eoff,"Help Filename has already been defined.");
  1852.           strcpy(hlp_fname, cmd+8);
  1853.           strupr(hlp_fname);
  1854.           }
  1855.  
  1856.            else if ( strnicmp(cmd, "Version=", 8) == 0 )
  1857.           {
  1858.           if (version != -1)   /* an unlikely value */
  1859.              warn(eoff,"Help version has already been defined");
  1860.           version = atoi(cmd+8);
  1861.           }
  1862.  
  1863.            else
  1864.           error(eoff,"Bad or unexpected command \"%s\"", cmd);
  1865.  
  1866.            continue;
  1867.            }
  1868.  
  1869.  
  1870.         /* commands allowed only in a topic... */
  1871.  
  1872.         else
  1873.            {
  1874.            if (strnicmp(cmd, "FF", 2) == 0 )
  1875.           {
  1876.           check_command_length(eoff,2);
  1877.           if ( in_para )
  1878.              *curr++ = '\n';  /* finish off current paragraph */
  1879.           *curr++ = CMD_FF;
  1880.           state = S_Start;
  1881.           in_para = 0;
  1882.           num_spaces = 0;
  1883.           }
  1884.  
  1885.            else if (strnicmp(cmd, "DocFF", 5) == 0 )
  1886.           {
  1887.           check_command_length(eoff,5);
  1888.           if ( in_para )
  1889.              *curr++ = '\n';  /* finish off current paragraph */
  1890.           if (!xonline)
  1891.              *curr++ = CMD_XONLINE;
  1892.           *curr++ = CMD_FF;
  1893.           if (!xonline)
  1894.              *curr++ = CMD_XONLINE;
  1895.           state = S_Start;
  1896.           in_para = 0;
  1897.           num_spaces = 0;
  1898.           }
  1899.  
  1900.            else if (strnicmp(cmd, "OnlineFF", 8) == 0 )
  1901.           {
  1902.           check_command_length(eoff,8);
  1903.           if ( in_para )
  1904.              *curr++ = '\n';  /* finish off current paragraph */
  1905.           if (!xdoc)
  1906.              *curr++ = CMD_XDOC;
  1907.           *curr++ = CMD_FF;
  1908.           if (!xdoc)
  1909.              *curr++ = CMD_XDOC;
  1910.           state = S_Start;
  1911.           in_para = 0;
  1912.           num_spaces = 0;
  1913.           }
  1914.  
  1915.            else if ( strnicmp(cmd, "Label=", 6) == 0 )
  1916.           {
  1917.           if (strlen(cmd+6) <= 0)
  1918.              error(eoff,"Label has no name.");
  1919.  
  1920.           else if ( !validate_label_name(cmd+6) )
  1921.              error(eoff,"Label \"%s\" contains illegal characters.", cmd+6);
  1922.  
  1923.           else if ( find_label(cmd+6) != NULL )
  1924.              error(eoff,"Label \"%s\" already exists", cmd+6);
  1925.  
  1926.           else
  1927.              {
  1928.              if (strlen(cmd+6) > 32)
  1929.                 warn(eoff,"Label name is long.");
  1930.  
  1931.             if ( (t.flags & TF_DATA) && cmd[6] == '@' )
  1932.                warn(eoff, "Data topic has a local label.");
  1933.  
  1934.              lbl.name       = dupstr(cmd+6, 0);
  1935.              lbl.topic_num = num_topic;
  1936.              lbl.topic_off = (unsigned)(curr - buffer);
  1937.              lbl.doc_page  = -1;
  1938.              add_label(&lbl);
  1939.              }
  1940.           }
  1941.  
  1942.            else if ( strnicmp(cmd, "Table=", 6) == 0 )
  1943.           {
  1944.           if ( in_para )
  1945.              {
  1946.              *curr++ = '\n';  /* finish off current paragraph */
  1947.              in_para = 0;
  1948.              num_spaces = 0;
  1949.              state = S_Start;
  1950.              }
  1951.  
  1952.           if (!done)
  1953.              {
  1954.              if (imbedded)
  1955.                 unread_char('(');
  1956.              unread_char('~');
  1957.              done = 1;
  1958.              }
  1959.  
  1960.           create_table();
  1961.           }
  1962.  
  1963.            else if ( strnicmp(cmd, "FormatExclude", 12) == 0 )
  1964.           {
  1965.           if (cmd[13] == '-')
  1966.              {
  1967.              check_command_length(eoff,14);
  1968.              if (lformat_exclude > 0)
  1969.                 lformat_exclude = -lformat_exclude;
  1970.              else
  1971.                 warn(0,"\"FormatExclude-\" is already in effect.");
  1972.              }
  1973.           else if (cmd[13] == '+')
  1974.              {
  1975.              check_command_length(eoff,14);
  1976.              if (lformat_exclude < 0)
  1977.                 lformat_exclude = -lformat_exclude;
  1978.              else
  1979.                 warn(0,"\"FormatExclude+\" is already in effect.");
  1980.              }
  1981.           else
  1982.              error(eoff,"Unexpected or invalid argument to FormatExclude.");
  1983.           }
  1984.  
  1985.            else if ( strnicmp(cmd, "Format", 6) == 0 )
  1986.           {
  1987.           if (cmd[6] == '+')
  1988.              {
  1989.              check_command_length(eoff,7);
  1990.              if ( !formatting )
  1991.                 {
  1992.                 formatting = 1;
  1993.                 in_para = 0;
  1994.                 num_spaces = 0;
  1995.                 state = S_Start;
  1996.                 }
  1997.              else
  1998.                 warn(eoff,"\"Format+\" is already in effect.");
  1999.              }
  2000.           else if (cmd[6] == '-')
  2001.              {
  2002.              check_command_length(eoff,7);
  2003.              if ( formatting )
  2004.                 {
  2005.                 if ( in_para )
  2006.                *curr++ = '\n';  /* finish off current paragraph */
  2007.                 state = S_Start;
  2008.                 in_para = 0;
  2009.                 formatting = 0;
  2010.                 num_spaces = 0;
  2011.                 state = S_Start;
  2012.                 }
  2013.              else
  2014.                 warn(eoff,"\"Format-\" is already in effect.");
  2015.              }
  2016.           else
  2017.              error(eoff,"Invalid argument to Format.");
  2018.           }
  2019.  
  2020.            else if ( strnicmp(cmd, "Online", 6) == 0 )
  2021.           {
  2022.           if (cmd[6] == '+')
  2023.              {
  2024.              check_command_length(eoff,7);
  2025.  
  2026.              if ( xonline )
  2027.                 {
  2028.                 *curr++ = CMD_XONLINE;
  2029.                 xonline = 0;
  2030.                 }
  2031.              else
  2032.                 warn(eoff,"\"Online+\" already in effect.");
  2033.              }
  2034.           else if (cmd[6] == '-')
  2035.              {
  2036.              check_command_length(eoff,7);
  2037.              if ( !xonline )
  2038.                 {
  2039.                 *curr++ = CMD_XONLINE;
  2040.                 xonline = 1;
  2041.                 }
  2042.              else
  2043.                 warn(eoff,"\"Online-\" already in effect.");
  2044.              }
  2045.           else
  2046.              error(eoff,"Invalid argument to Online.");
  2047.           }
  2048.  
  2049.            else if ( strnicmp(cmd, "Doc", 3) == 0 )
  2050.           {
  2051.           if (cmd[3] == '+')
  2052.              {
  2053.              check_command_length(eoff,4);
  2054.              if ( xdoc )
  2055.                 {
  2056.                 *curr++ = CMD_XDOC;
  2057.                 xdoc = 0;
  2058.                 }
  2059.              else
  2060.                 warn(eoff,"\"Doc+\" already in effect.");
  2061.              }
  2062.           else if (cmd[3] == '-')
  2063.              {
  2064.              check_command_length(eoff,4);
  2065.              if ( !xdoc )
  2066.                 {
  2067.                 *curr++ = CMD_XDOC;
  2068.                 xdoc = 1;
  2069.                 }
  2070.              else
  2071.                 warn(eoff,"\"Doc-\" already in effect.");
  2072.              }
  2073.           else
  2074.              error(eoff,"Invalid argument to Doc.");
  2075.           }
  2076.  
  2077.            else if ( strnicmp(cmd, "Center", 6) == 0 )
  2078.           {
  2079.           if (cmd[6] == '+')
  2080.              {
  2081.              check_command_length(eoff,7);
  2082.              if ( !centering )
  2083.                 {
  2084.                 centering = 1;
  2085.                 if ( in_para )
  2086.                {
  2087.                *curr++ = '\n';
  2088.                in_para = 0;
  2089.                }
  2090.                 state = S_Start;  /* for centering FSM */
  2091.                 }
  2092.              else
  2093.                 warn(eoff,"\"Center+\" already in effect.");
  2094.              }
  2095.           else if (cmd[6] == '-')
  2096.              {
  2097.              check_command_length(eoff,7);
  2098.              if ( centering )
  2099.                 {
  2100.                 centering = 0;
  2101.                 state = S_Start;  /* for centering FSM */
  2102.                 }
  2103.              else
  2104.                 warn(eoff,"\"Center-\" already in effect.");
  2105.              }
  2106.           else
  2107.              error(eoff,"Invalid argument to Center.");
  2108.           }
  2109.  
  2110.            else if ( strnicmp(cmd, "CompressSpaces", 14) == 0 )
  2111.           {
  2112.           check_command_length(eoff,15);
  2113.  
  2114.           if ( cmd[14] == '+' )
  2115.              {
  2116.              if ( compress_spaces )
  2117.                 warn(eoff,"\"CompressSpaces+\" is already in effect.");
  2118.              else
  2119.                 compress_spaces = 1;
  2120.              }
  2121.           else if ( cmd[14] == '-' )
  2122.              {
  2123.              if ( !compress_spaces )
  2124.                 warn(eoff,"\"CompressSpaces-\" is already in effect.");
  2125.              else
  2126.                 compress_spaces = 0;
  2127.              }
  2128.           else
  2129.              error(eoff,"Invalid argument to CompressSpaces.");
  2130.           }
  2131.  
  2132.            else if ( strnicmp("BinInc ", cmd, 7) == 0 )
  2133.           {
  2134.           if ( !(t.flags & TF_DATA) )
  2135.              error(eoff,"BinInc allowed only in Data topics.");
  2136.           else
  2137.              process_bininc();
  2138.           }
  2139.  
  2140.            else
  2141.           error(eoff,"Bad or unexpected command \"%s\".", cmd);
  2142.            } /* else */
  2143.  
  2144.         } /* while (!done) */
  2145.  
  2146.      continue;
  2147.      }
  2148.  
  2149.       if ( !in_topic )
  2150.      {
  2151.      cmd[0] = ch;
  2152.      ptr = read_until(cmd+1, 127, "\n~");
  2153.      if (*ptr == '~')
  2154.         unread_char('~');
  2155.      *ptr = '\0';
  2156.      error(0,"Text outside of any topic \"%s\".", cmd);
  2157.      continue;
  2158.      }
  2159.  
  2160.       if ( centering )
  2161.      {
  2162.      do
  2163.         {
  2164.         again = 0;     /* default */
  2165.  
  2166.         switch (state)
  2167.            {
  2168.            case S_Start:
  2169.           if (ch == ' ')
  2170.              ; /* do nothing */
  2171.           else if ( (ch&0xFF) == '\n' )
  2172.              *curr++ = ch;  /* no need to center blank lines. */
  2173.           else
  2174.              {
  2175.              *curr++ = CMD_CENTER;
  2176.              state = S_Line;
  2177.              again = 1;
  2178.              }
  2179.           break;
  2180.  
  2181.            case S_Line:
  2182.           put_a_char(ch, &t);
  2183.           if ( (ch&0xFF) == '\n')
  2184.              state = S_Start;
  2185.           break;
  2186.            } /* switch */
  2187.         }
  2188.      while (again);
  2189.      }
  2190.  
  2191.       else if ( formatting )
  2192.      {
  2193.      int again;
  2194.  
  2195.      do
  2196.         {
  2197.         again = 0;     /* default */
  2198.  
  2199.         switch (state)
  2200.            {
  2201.            case S_Start:
  2202.           if ( (ch&0xFF) == '\n' )
  2203.              *curr++ = ch;
  2204.           else
  2205.              {
  2206.              state = S_StartFirstLine;
  2207.              num_spaces = 0;
  2208.              again = 1;
  2209.              }
  2210.           break;
  2211.  
  2212.            case S_StartFirstLine:
  2213.           if ( ch == ' ')
  2214.              ++num_spaces;
  2215.  
  2216.           else
  2217.              {
  2218.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2219.                 {
  2220.                 put_spaces(num_spaces);
  2221.                 num_spaces = 0;
  2222.                 state = S_FormatDisabled;
  2223.                 again = 1;
  2224.                 }
  2225.              else
  2226.                 {
  2227.                 *curr++ = CMD_PARA;
  2228.                 *curr++ = (char)num_spaces;
  2229.                 *curr++ = (char)num_spaces;
  2230.                 margin_pos = curr - 1;
  2231.                 state = S_FirstLine;
  2232.                 again = 1;
  2233.                 in_para = 1;
  2234.                 }
  2235.              }
  2236.           break;
  2237.  
  2238.            case S_FirstLine:
  2239.           if (ch == '\n')
  2240.              {
  2241.              state = S_StartSecondLine;
  2242.              num_spaces = 0;
  2243.              }
  2244.           else if (ch == ('\n'|0x100) )   /* force end of para ? */
  2245.              {
  2246.              *curr++ = '\n';
  2247.              in_para = 0;
  2248.              state = S_Start;
  2249.              }
  2250.           else if ( ch == ' ' )
  2251.              {
  2252.              state = S_FirstLineSpaces;
  2253.              num_spaces = 1;
  2254.              }
  2255.           else
  2256.              put_a_char(ch, &t);
  2257.           break;
  2258.  
  2259.            case S_FirstLineSpaces:
  2260.           if (ch == ' ')
  2261.              ++num_spaces;
  2262.           else
  2263.              {
  2264.              put_spaces(num_spaces);
  2265.              state = S_FirstLine;
  2266.              again = 1;
  2267.              }
  2268.           break;
  2269.  
  2270.            case S_StartSecondLine:
  2271.           if ( ch == ' ')
  2272.              ++num_spaces;
  2273.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2274.              {
  2275.              *curr++ = '\n';   /* end the para */
  2276.              *curr++ = '\n';   /* for the blank line */
  2277.              in_para = 0;
  2278.              state = S_Start;
  2279.              }
  2280.           else
  2281.              {
  2282.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2283.                 {
  2284.                 *curr++ = '\n';
  2285.                 in_para = 0;
  2286.                 put_spaces(num_spaces);
  2287.                 num_spaces = 0;
  2288.                 state = S_FormatDisabled;
  2289.                 again = 1;
  2290.                 }
  2291.              else
  2292.                 {
  2293.                 add_blank_for_split();
  2294.                 margin = num_spaces;
  2295.                 *margin_pos = (char)num_spaces;
  2296.                 state = S_Line;
  2297.                 again = 1;
  2298.                 }
  2299.              }
  2300.           break;
  2301.  
  2302.            case S_Line:   /* all lines after the first */
  2303.           if (ch == '\n')
  2304.              {
  2305.              state = S_StartLine;
  2306.              num_spaces = 0;
  2307.              }
  2308.           else if (ch == ('\n' | 0x100) )   /* force end of para ? */
  2309.              {
  2310.              *curr++ = '\n';
  2311.              in_para = 0;
  2312.              state = S_Start;
  2313.              }
  2314.           else if ( ch == ' ' )
  2315.              {
  2316.              state = S_LineSpaces;
  2317.              num_spaces = 1;
  2318.              }
  2319.           else
  2320.              put_a_char(ch, &t);
  2321.           break;
  2322.  
  2323.            case S_LineSpaces:
  2324.           if (ch == ' ')
  2325.              ++num_spaces;
  2326.           else
  2327.              {
  2328.              put_spaces(num_spaces);
  2329.              state = S_Line;
  2330.              again = 1;
  2331.              }
  2332.           break;
  2333.  
  2334.            case S_StartLine:   /* for all lines after the second */
  2335.           if ( ch == ' ')
  2336.              ++num_spaces;
  2337.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2338.              {
  2339.              *curr++ = '\n';   /* end the para */
  2340.              *curr++ = '\n';   /* for the blank line */
  2341.              in_para = 0;
  2342.              state = S_Start;
  2343.              }
  2344.           else
  2345.              {
  2346.              if ( num_spaces != margin )
  2347.                 {
  2348.                 *curr++ = '\n';
  2349.                 in_para = 0;
  2350.                 state = S_StartFirstLine;  /* with current num_spaces */
  2351.                 again = 1;
  2352.                 }
  2353.              else
  2354.                 {
  2355.                 add_blank_for_split();
  2356.                 state = S_Line;
  2357.                 again = 1;
  2358.                 }
  2359.              }
  2360.           break;
  2361.  
  2362.            case S_FormatDisabled:
  2363.           if ( ch == ' ' )
  2364.              {
  2365.              state = S_FormatDisabledSpaces;
  2366.              num_spaces = 1;
  2367.              }
  2368.           else
  2369.              {
  2370.              if ( (ch&0xFF) == '\n' )
  2371.                 state = S_Start;
  2372.              put_a_char(ch, &t);
  2373.              }
  2374.           break;
  2375.  
  2376.            case S_FormatDisabledSpaces:
  2377.           if ( ch == ' ' )
  2378.              ++num_spaces;
  2379.           else
  2380.              {
  2381.              put_spaces(num_spaces);
  2382.              num_spaces = 0;    /* is this needed? */
  2383.              state = S_FormatDisabled;
  2384.              again = 1;
  2385.              }
  2386.           break;
  2387.  
  2388.            } /* switch (state) */
  2389.         }
  2390.      while (again);
  2391.      }
  2392.  
  2393.       else
  2394.      {
  2395.      do
  2396.         {
  2397.         again = 0;     /* default */
  2398.  
  2399.         switch (state)
  2400.            {
  2401.            case S_Start:
  2402.           if ( ch == ' ' )
  2403.              {
  2404.              state = S_Spaces;
  2405.              num_spaces = 1;
  2406.              }
  2407.           else
  2408.              put_a_char(ch, &t);
  2409.           break;
  2410.  
  2411.            case S_Spaces:
  2412.           if (ch == ' ')
  2413.              ++num_spaces;
  2414.           else
  2415.              {
  2416.              put_spaces(num_spaces);
  2417.              num_spaces = 0;     /* is this needed? */
  2418.              state = S_Start;
  2419.              again = 1;
  2420.              }
  2421.           break;
  2422.            } /* switch */
  2423.         }
  2424.      while (again);
  2425.      }
  2426.  
  2427.       CHK_BUFFER(0)
  2428.       } /* while ( 1 ) */
  2429.  
  2430.    fclose(srcfile);
  2431.  
  2432.    srcline = -1;
  2433.    }
  2434.  
  2435.  
  2436. /*
  2437.  * stuff to resolve hot-link references.
  2438.  */
  2439.  
  2440.  
  2441. void make_hot_links(void)
  2442.    /*
  2443.     * calculate topic_num/topic_off for each link.
  2444.     */
  2445.    {
  2446.    LINK    *l;
  2447.    LABEL   *lbl;
  2448.    int        lctr;
  2449.    int        t;
  2450.    CONTENT *c;
  2451.    int        ctr;
  2452.  
  2453.    msg("Making hot-links.");
  2454.  
  2455.    /*
  2456.     * Calculate topic_num for all entries in DocContents.  Also set
  2457.     * "TF_IN_DOC" flag for all topics included in the document.
  2458.     */
  2459.  
  2460.    for (lctr=0, c=contents; lctr<num_contents; lctr++, c++)
  2461.       {
  2462.       for (ctr=0; ctr<c->num_topic; ctr++)
  2463.      {
  2464.      if ( c->is_label[ctr] )
  2465.         {
  2466.         lbl = find_label(c->topic_name[ctr]);
  2467.         if (lbl == NULL)
  2468.            {
  2469.            src_cfname = c->srcfile;
  2470.            srcline = c->srcline;
  2471.            error(0,"Cannot find DocContent label \"%s\".", c->topic_name[ctr]);
  2472.            srcline = -1;
  2473.            }
  2474.         else
  2475.            {
  2476.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2477.           {
  2478.           src_cfname = c->srcfile;
  2479.           srcline = c->srcline;
  2480.           error(0,"Label \"%s\" is a data-only topic.", c->topic_name[ctr]);
  2481.           srcline = -1;
  2482.           }
  2483.            else
  2484.           {
  2485.           c->topic_num[ctr] = lbl->topic_num;
  2486.           if ( topic[lbl->topic_num].flags & TF_IN_DOC )
  2487.              warn(0,"Topic \"%s\" appears in document more than once.",
  2488.               topic[lbl->topic_num].title);
  2489.           else
  2490.              topic[lbl->topic_num].flags |= TF_IN_DOC;
  2491.           }
  2492.            }
  2493.  
  2494.         }
  2495.      else
  2496.         {
  2497.         t = find_topic_title(c->topic_name[ctr]);
  2498.  
  2499.         if (t == -1)
  2500.            {
  2501.            src_cfname = c->srcfile;
  2502.            srcline = c->srcline;
  2503.            error(0,"Cannot find DocContent topic \"%s\".", c->topic_name[ctr]);
  2504.            srcline = -1;  /* back to reality */
  2505.            }
  2506.         else
  2507.            {
  2508.            c->topic_num[ctr] = t;
  2509.            if ( topic[t].flags & TF_IN_DOC )
  2510.           warn(0,"Topic \"%s\" appears in document more than once.",
  2511.                topic[t].title);
  2512.            else
  2513.           topic[t].flags |= TF_IN_DOC;
  2514.            }
  2515.         }
  2516.      }
  2517.       }
  2518.  
  2519.    /*
  2520.     * Find topic_num and topic_off for all hot-links.  Also flag all hot-
  2521.     * links which will (probably) appear in the document.
  2522.     */
  2523.  
  2524.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2525.       {
  2526.       switch ( l->type )
  2527.      {
  2528.      case 0:      /* name is the title of the topic */
  2529.         t = find_topic_title(l->name);
  2530.         if (t == -1)
  2531.            {
  2532.            src_cfname = l->srcfile;
  2533.            srcline = l->srcline; /* pretend we are still in the source... */
  2534.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2535.            srcline = -1;  /* back to reality */
  2536.            }
  2537.         else
  2538.            {
  2539.            l->topic_num = t;
  2540.            l->topic_off = 0;
  2541.            l->doc_page = (topic[t].flags & TF_IN_DOC) ? 0 : -1;
  2542.            }
  2543.         break;
  2544.  
  2545.      case 1:  /* name is the name of a label */
  2546.         lbl = find_label(l->name);
  2547.         if (lbl == NULL)
  2548.            {
  2549.            src_cfname = l->srcfile;
  2550.            srcline = l->srcline; /* pretend again */
  2551.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2552.            srcline = -1;
  2553.            }
  2554.         else
  2555.            {
  2556.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2557.           {
  2558.           src_cfname = l->srcfile;
  2559.           srcline = l->srcline;
  2560.           error(0,"Label \"%s\" is a data-only topic.", l->name);
  2561.           srcline = -1;
  2562.           }
  2563.            else
  2564.           {
  2565.           l->topic_num = lbl->topic_num;
  2566.           l->topic_off = lbl->topic_off;
  2567.           l->doc_page  = (topic[lbl->topic_num].flags & TF_IN_DOC) ? 0 : -1;
  2568.           }
  2569.            }
  2570.         break;
  2571.  
  2572.      case 2:   /* it's a "special" link; topic_off already has the value */
  2573.         break;
  2574.      }
  2575.       }
  2576.  
  2577.    }
  2578.  
  2579.  
  2580. /*
  2581.  * online help pagination stuff
  2582.  */
  2583.  
  2584.  
  2585. void add_page_break(TOPIC *t, int margin, char *text, char *start, char *curr, int num_links)
  2586.    {
  2587.    PAGE p;
  2588.  
  2589.    p.offset = (unsigned) (start - text);
  2590.    p.length = (unsigned) (curr - start);
  2591.    p.margin = margin;
  2592.    add_page(t, &p);
  2593.  
  2594.    if (max_links < num_links)
  2595.       max_links = num_links;
  2596.    }
  2597.  
  2598.  
  2599. void paginate_online(void)    /* paginate the text for on-line help */
  2600.    {               /* also calculates max_pages and max_links */
  2601.    int         lnum;
  2602.    char     *start;
  2603.    char     *curr;
  2604.    char     *text;
  2605.    TOPIC    *t;
  2606.    int         tctr;
  2607.    unsigned  len;
  2608.    int         skip_blanks;
  2609.    int         num_links;
  2610.    int         col;
  2611.    int         tok;
  2612.    int         size,
  2613.          width;
  2614.    int         start_margin;
  2615.  
  2616.    msg("Paginating online help.");
  2617.  
  2618.    for (t=topic, tctr=0; tctr<num_topic; t++, tctr++)
  2619.       {
  2620.       if ( t->flags & TF_DATA )
  2621.      continue;    /* don't paginate data topics */
  2622.  
  2623.       text = get_topic_text(t);
  2624.       curr = text;
  2625.       len  = t->text_len;
  2626.  
  2627.       start = curr;
  2628.       skip_blanks = 0;
  2629.       lnum = 0;
  2630.       num_links = 0;
  2631.       col = 0;
  2632.       start_margin = -1;
  2633.  
  2634.       while (len > 0)
  2635.      {
  2636.      tok = find_token_length(ONLINE, curr, len, &size, &width);
  2637.  
  2638.      switch ( tok )
  2639.         {
  2640.         case TOK_PARA:
  2641.            {
  2642.            int indent,
  2643.            margin;
  2644.  
  2645.            ++curr;
  2646.  
  2647.            indent = *curr++;
  2648.            margin = *curr++;
  2649.  
  2650.            len -= 3;
  2651.  
  2652.            col = indent;
  2653.  
  2654.            while (1)
  2655.           {
  2656.           tok = find_token_length(ONLINE, curr, len, &size, &width);
  2657.  
  2658.           if (tok == TOK_DONE || tok == TOK_NL || tok == TOK_FF )
  2659.              break;
  2660.  
  2661.           if ( tok == TOK_PARA )
  2662.              {
  2663.              col = 0;   /* fake a nl */
  2664.              ++lnum;
  2665.              break;
  2666.              }
  2667.  
  2668.           if (tok == TOK_XONLINE || tok == TOK_XDOC )
  2669.              {
  2670.              curr += size;
  2671.              len -= size;
  2672.              continue;
  2673.              }
  2674.  
  2675.           /* now tok is TOK_SPACE or TOK_LINK or TOK_WORD */
  2676.  
  2677.           if (col+width > SCREEN_WIDTH)
  2678.              {            /* go to next line... */
  2679.              if ( ++lnum >= SCREEN_DEPTH )
  2680.                 {        /* go to next page... */
  2681.                 add_page_break(t, start_margin, text, start, curr, num_links);
  2682.                 start = curr + ( (tok == TOK_SPACE) ? size : 0 );
  2683.                 start_margin = margin;
  2684.                 lnum = 0;
  2685.                 num_links = 0;
  2686.                 }
  2687.              if ( tok == TOK_SPACE )
  2688.                 width = 0;   /* skip spaces at start of a line */
  2689.  
  2690.              col = margin;
  2691.              }
  2692.  
  2693.           col += width;
  2694.           curr += size;
  2695.           len -= size;
  2696.           }
  2697.  
  2698.            skip_blanks = 0;
  2699.            width = size = 0;
  2700.            break;
  2701.            }
  2702.  
  2703.         case TOK_NL:
  2704.            if (skip_blanks && col == 0)
  2705.           {
  2706.           start += size;
  2707.           break;
  2708.           }
  2709.            ++lnum;
  2710.            if ( lnum >= SCREEN_DEPTH || (col == 0 && lnum==SCREEN_DEPTH-1) )
  2711.           {
  2712.           add_page_break(t, start_margin, text, start, curr, num_links);
  2713.           start = curr + size;
  2714.           start_margin = -1;
  2715.           lnum = 0;
  2716.           num_links = 0;
  2717.           skip_blanks = 1;
  2718.           }
  2719.            col = 0;
  2720.            break;
  2721.  
  2722.         case TOK_FF:
  2723.            col = 0;
  2724.            if (skip_blanks)
  2725.           {
  2726.           start += size;
  2727.           break;
  2728.           }
  2729.            add_page_break(t, start_margin, text, start, curr, num_links);
  2730.            start_margin = -1;
  2731.            start = curr + size;
  2732.            lnum = 0;
  2733.            num_links = 0;
  2734.            break;
  2735.  
  2736.         case TOK_DONE:
  2737.         case TOK_XONLINE:   /* skip */
  2738.         case TOK_XDOC:      /* ignore */
  2739.         case TOK_CENTER:    /* ignore */
  2740.            break;
  2741.  
  2742.         case TOK_LINK:
  2743.            ++num_links;
  2744.  
  2745.            /* fall-through */
  2746.  
  2747.         default:    /* TOK_SPACE, TOK_LINK, TOK_WORD */
  2748.            skip_blanks = 0;
  2749.            break;
  2750.  
  2751.         } /* switch */
  2752.  
  2753.      curr += size;
  2754.      len  -= size;
  2755.      col  += width;
  2756.      } /* while */
  2757.  
  2758.       if (!skip_blanks)
  2759.      add_page_break(t, start_margin, text, start, curr, num_links);
  2760.  
  2761.       if (max_pages < t->num_page)
  2762.      max_pages = t->num_page;
  2763.  
  2764.       release_topic_text(t, 0);
  2765.       } /* for */
  2766.    }
  2767.  
  2768.  
  2769. /*
  2770.  * paginate document stuff
  2771.  */
  2772.  
  2773.  
  2774. #define CNUM           0
  2775. #define TNUM           1
  2776. #define LINK_DEST_WARN 2
  2777.  
  2778.  
  2779. typedef struct
  2780.    {
  2781.    int        cnum,  /* must match above #defines so pd_get_info() will work */
  2782.         tnum,
  2783.         link_dest_warn;
  2784.  
  2785.    char far *start;
  2786.    CONTENT  *c;
  2787.    LABEL    *lbl;
  2788.  
  2789.    } PAGINATE_DOC_INFO;
  2790.  
  2791.  
  2792. LABEL *find_next_label_by_topic(int t)
  2793.    {
  2794.    LABEL *temp, *g, *p;
  2795.    int      ctr;
  2796.  
  2797.    g = p = NULL;
  2798.  
  2799.    for (temp=label, ctr=0; ctr<num_label; ctr++, temp++)
  2800.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2801.      {
  2802.      g = temp;
  2803.      break;
  2804.      }
  2805.       else if (temp->topic_num > t)
  2806.      break;
  2807.  
  2808.    for (temp=plabel, ctr=0; ctr<num_plabel; ctr++, temp++)
  2809.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2810.      {
  2811.      p = temp;
  2812.      break;
  2813.      }
  2814.       else if (temp->topic_num > t)
  2815.      break;
  2816.  
  2817.    if ( p == NULL )
  2818.       return (g);
  2819.  
  2820.    else if ( g == NULL )
  2821.       return (p);
  2822.  
  2823.    else
  2824.       return ( (g->topic_off < p->topic_off) ? g : p );
  2825.    }
  2826.  
  2827.  
  2828. void set_hot_link_doc_page(void)
  2829.    /*
  2830.     * Find doc_page for all hot-links.
  2831.     */
  2832.    {
  2833.    LINK  *l;
  2834.    LABEL *lbl;
  2835.    int      lctr;
  2836.    int      t;
  2837.  
  2838.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2839.       {
  2840.       switch ( l->type )
  2841.      {
  2842.      case 0:      /* name is the title of the topic */
  2843.         t = find_topic_title(l->name);
  2844.         if (t == -1)
  2845.            {
  2846.            src_cfname = l->srcfile;
  2847.            srcline = l->srcline; /* pretend we are still in the source... */
  2848.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2849.            srcline = -1;  /* back to reality */
  2850.            }
  2851.         else
  2852.            l->doc_page = topic[t].doc_page;
  2853.         break;
  2854.  
  2855.      case 1:  /* name is the name of a label */
  2856.         lbl = find_label(l->name);
  2857.         if (lbl == NULL)
  2858.            {
  2859.            src_cfname = l->srcfile;
  2860.            srcline = l->srcline; /* pretend again */
  2861.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2862.            srcline = -1;
  2863.            }
  2864.         else
  2865.            l->doc_page = lbl->doc_page;
  2866.         break;
  2867.  
  2868.      case 2:   /* special topics don't appear in the document */
  2869.         break;
  2870.      }
  2871.       }
  2872.    }
  2873.  
  2874.  
  2875. void set_content_doc_page(void)
  2876.    /*
  2877.     * insert page #'s in the DocContents
  2878.     */
  2879.    {
  2880.    CONTENT *c;
  2881.    TOPIC   *t;
  2882.    char    *base;
  2883.    int        tnum;
  2884.    int        ctr;
  2885.    char     buf[4];
  2886.    int        len;
  2887.  
  2888.    tnum = find_topic_title(DOCCONTENTS_TITLE);
  2889.    assert(tnum>=0);
  2890.    t = &topic[tnum];
  2891.  
  2892.    base = get_topic_text(t);
  2893.  
  2894.    for (ctr=1, c=contents+1; ctr<num_contents; ctr++, c++)
  2895.       {
  2896.       assert(c->doc_page>=1);
  2897.       sprintf(buf, "%d", c->doc_page);
  2898.       len = strlen(buf);
  2899.       assert(len<=3);
  2900.       memcpy(base+c->page_num_pos+(3-len), buf, len);
  2901.       }
  2902.  
  2903.    release_topic_text(t, 1);
  2904.    }
  2905.  
  2906.  
  2907. int pd_get_info(int cmd, PD_INFO *pd, int *info)
  2908.    {         /* this funtion also used by print_document() */
  2909.    CONTENT *c;
  2910.  
  2911.    switch (cmd)
  2912.       {
  2913.       case PD_GET_CONTENT:
  2914.      if ( ++info[CNUM] >= num_contents )
  2915.         return (0);
  2916.      c = &contents[info[CNUM]];
  2917.      info[TNUM] = -1;
  2918.      pd->id       = c->id;
  2919.      pd->title    = c->name;
  2920.      pd->new_page = (c->flags & CF_NEW_PAGE) ? 1 : 0;
  2921.      return (1);
  2922.  
  2923.       case PD_GET_TOPIC:
  2924.      c = &contents[info[CNUM]];
  2925.      if ( ++info[TNUM] >= c->num_topic )
  2926.         return (0);
  2927.      pd->curr = get_topic_text( &topic[c->topic_num[info[TNUM]]] );
  2928.      pd->len = topic[c->topic_num[info[TNUM]]].text_len;
  2929.      return (1);
  2930.  
  2931.       case PD_GET_LINK_PAGE:
  2932.          if ( a_link[getint(pd->s)].doc_page == -1 )
  2933.         {
  2934.         if ( info[LINK_DEST_WARN] )
  2935.            {
  2936.                src_cfname = a_link[getint(pd->s)].srcfile;
  2937.                srcline    = a_link[getint(pd->s)].srcline;
  2938.            warn(0,"Hot-link destination is not in the document.");
  2939.            srcline = -1;
  2940.            }
  2941.         return (0);
  2942.         }
  2943.          pd->i = a_link[getint(pd->s)].doc_page;
  2944.      return (1);
  2945.  
  2946.       case PD_RELEASE_TOPIC:
  2947.      c = &contents[info[CNUM]];
  2948.      release_topic_text(&topic[c->topic_num[info[TNUM]]], 0);
  2949.      return (1);
  2950.  
  2951.       default:
  2952.      return (0);
  2953.       }
  2954.    }
  2955.  
  2956.  
  2957. int paginate_doc_output(int cmd, PD_INFO *pd, PAGINATE_DOC_INFO *info)
  2958.    {
  2959.    switch (cmd)
  2960.       {
  2961.       case PD_FOOTING:
  2962.       case PD_PRINT:
  2963.       case PD_PRINTN:
  2964.       case PD_PRINT_SEC:
  2965.      return (1);
  2966.  
  2967.       case PD_HEADING:
  2968.      ++num_doc_pages;
  2969.      return (1);
  2970.  
  2971.       case PD_START_SECTION:
  2972.      info->c = &contents[info->cnum];
  2973.      return (1);
  2974.  
  2975.       case PD_START_TOPIC:
  2976.      info->start = pd->curr;
  2977.      info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2978.      return (1);
  2979.  
  2980.       case PD_SET_SECTION_PAGE:
  2981.      info->c->doc_page = pd->pnum;
  2982.      return (1);
  2983.  
  2984.       case PD_SET_TOPIC_PAGE:
  2985.      topic[info->c->topic_num[info->tnum]].doc_page = pd->pnum;
  2986.      return (1);
  2987.  
  2988.       case PD_PERIODIC:
  2989.      while ( info->lbl != NULL && (unsigned)(pd->curr - info->start) >= info->lbl->topic_off)
  2990.         {
  2991.         info->lbl->doc_page = pd->pnum;
  2992.         info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2993.         }
  2994.      return (1);
  2995.  
  2996.       default:
  2997.      return (0);
  2998.       }
  2999.    }
  3000.  
  3001.  
  3002. void paginate_document(void)
  3003.    {
  3004.    PAGINATE_DOC_INFO info;
  3005.  
  3006.    if (num_contents == 0)
  3007.       return ;
  3008.  
  3009.    msg("Paginating document.");
  3010.  
  3011.    info.cnum = info.tnum = -1;
  3012.    info.link_dest_warn = 1;
  3013.  
  3014.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)paginate_doc_output, &info);
  3015.  
  3016.    set_hot_link_doc_page();
  3017.    set_content_doc_page();
  3018.    }
  3019.  
  3020.  
  3021. /*
  3022.  * label sorting stuff
  3023.  */
  3024.  
  3025.  
  3026. int fcmp_LABEL(VOIDCONSTPTR a, VOIDCONSTPTR b)
  3027.    {
  3028.    char *an = ((LABEL *)a)->name,
  3029.         *bn = ((LABEL *)b)->name;
  3030.    int     diff;
  3031.  
  3032.    /* compare the names, making sure that the index goes first */
  3033.  
  3034.    if ( (diff=strcmp(an,bn)) == 0 )
  3035.       return (0);
  3036.  
  3037.    if ( strcmp(an, INDEX_LABEL) == 0 )
  3038.       return (-1);
  3039.  
  3040.    if ( strcmp(bn, INDEX_LABEL) == 0 )
  3041.       return (1);
  3042.  
  3043.    return ( diff );
  3044.    }
  3045.  
  3046.  
  3047. void sort_labels(void)
  3048.    {
  3049.    qsort(label,  num_label,  sizeof(LABEL), fcmp_LABEL);
  3050.    qsort(plabel, num_plabel, sizeof(LABEL), fcmp_LABEL);
  3051.    }
  3052.  
  3053.  
  3054. /*
  3055.  * file write stuff.
  3056.  */
  3057.  
  3058.  
  3059. int compare_files(FILE *f1, FILE *f2) /* returns TRUE if different */
  3060.    {
  3061.    if ( filelength(fileno(f1)) != filelength(fileno(f2)) )
  3062.       return (1);   /* different if sizes are not the same */
  3063.  
  3064.    while ( !feof(f1) && !feof(f2) )
  3065.       if ( getc(f1) != getc(f2) )
  3066.      return (1);
  3067.  
  3068.    return ( ( feof(f1) && feof(f2) ) ? 0 : 1);
  3069.    }
  3070.  
  3071.  
  3072. void _write_hdr(char *fname, FILE *file)
  3073.    {
  3074.    int ctr;
  3075.    char nfile[MAXFILE],
  3076.         next[MAXEXT];
  3077.  
  3078.    FNSPLIT(fname, NULL, NULL, nfile, next);
  3079.    fprintf(file, "\n/*\n * %s%s\n", nfile, next);
  3080.    FNSPLIT(src_fname, NULL, NULL, nfile, next);
  3081.    fprintf(file, " *\n * Contains #defines for help.\n *\n");
  3082.    fprintf(file, " * Generated by HC from: %s%s\n *\n */\n\n\n", nfile, next);
  3083.  
  3084.    fprintf(file, "/* current help file version */\n");
  3085.    fprintf(file, "\n");
  3086.    fprintf(file, "#define %-32s %3d\n", "HELP_VERSION", version);
  3087.    fprintf(file, "\n\n");
  3088.  
  3089.    fprintf(file, "/* labels */\n\n");
  3090.  
  3091.    for (ctr=0; ctr<num_label; ctr++)
  3092.       if (label[ctr].name[0] != '@')  /* if it's not a local label... */
  3093.      {
  3094.      fprintf(file, "#define %-32s %3d", label[ctr].name, ctr);
  3095.      if ( strcmp(label[ctr].name, INDEX_LABEL) == 0 )
  3096.         fprintf(file, "        /* index */");
  3097.      fprintf(file, "\n");
  3098.      }
  3099.  
  3100.    fprintf(file, "\n\n");
  3101.    }
  3102.  
  3103.  
  3104. void write_hdr(char *fname)
  3105.    {
  3106.    FILE *temp,
  3107.         *hdr;
  3108.  
  3109.    hdr = fopen(fname, "rt");
  3110.  
  3111.    if (hdr == NULL)
  3112.       {         /* if no prev. hdr file generate a new one */
  3113.       hdr = fopen(fname, "wt");
  3114.       if (hdr == NULL)
  3115.      fatal(0,"Cannot create \"%s\".", fname);
  3116.       msg("Writing: %s", fname);
  3117.       _write_hdr(fname, hdr);
  3118.       fclose(hdr);
  3119.       notice("FRACTINT must be re-compiled.");
  3120.       return ;
  3121.       }
  3122.  
  3123.    msg("Comparing: %s", fname);
  3124.  
  3125.    temp = fopen(TEMP_FNAME, "wt");
  3126.  
  3127.    if (temp == NULL)
  3128.       fatal(0,"Cannot create temporary file: \"%s\".", TEMP_FNAME);
  3129.  
  3130.    _write_hdr(fname, temp);
  3131.  
  3132.    fclose(temp);
  3133.    temp = fopen(TEMP_FNAME, "rt");
  3134.  
  3135.    if (temp == NULL)
  3136.       fatal(0,"Cannot open temporary file: \"%s\".", TEMP_FNAME);
  3137.  
  3138.    if ( compare_files(temp, hdr) )   /* if they are different... */
  3139.       {
  3140.       msg("Updating: %s", fname);
  3141.       fclose(temp);
  3142.       fclose(hdr);
  3143.       unlink(fname);           /* delete the old hdr file */
  3144.       rename(TEMP_FNAME, fname);   /* rename the temp to the hdr file */
  3145.       notice("FRACTINT must be re-compiled.");
  3146.       }
  3147.    else
  3148.       {   /* if they are the same leave the original alone. */
  3149.       fclose(temp);
  3150.       fclose(hdr);
  3151.       unlink(TEMP_FNAME);      /* delete the temp */
  3152.       }
  3153.    }
  3154.  
  3155.  
  3156. void calc_offsets(void)    /* calc file offset to each topic */
  3157.    {
  3158.    int        t;
  3159.    TOPIC   *tp;
  3160.    long     offset;
  3161.    CONTENT *cp;
  3162.    int        c;
  3163.  
  3164.    /* NOTE: offsets do NOT include 6 bytes for signature & version! */
  3165.  
  3166.    offset = sizeof(int) +           /* max_pages */
  3167.             sizeof(int) +           /* max_links */
  3168.             sizeof(int) +           /* num_topic */
  3169.             sizeof(int) +           /* num_label */
  3170.             sizeof(int) +           /* num_contents */
  3171.             sizeof(int) +           /* num_doc_pages */
  3172.             num_topic*sizeof(long) +/* offsets to each topic */
  3173.             num_label*2*sizeof(int);/* topic_num/topic_off for all public labels */
  3174.  
  3175.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3176.       offset += sizeof(int) +       /* flags */
  3177.             1 +            /* id length */
  3178.             strlen(cp->id) +    /* id text */
  3179.             1 +            /* name length */
  3180.             strlen(cp->name) +  /* name text */
  3181.             1 +            /* number of topics */
  3182.                 cp->num_topic*sizeof(int);    /* topic numbers */
  3183.  
  3184.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3185.       {
  3186.       tp->offset = offset;
  3187.       offset += (long)sizeof(int) + /* topic flags */
  3188.                 sizeof(int) +       /* number of pages */
  3189.                 tp->num_page*3*sizeof(int) +   /* page offset, length & starting margin */
  3190.             1 +            /* length of title */
  3191.             tp->title_len +     /* title */
  3192.                 sizeof(int) +       /* length of text */
  3193.             tp->text_len;        /* text */
  3194.       }
  3195.  
  3196.    }
  3197.  
  3198.  
  3199. void insert_real_link_info(char *curr, unsigned len)
  3200.    /*
  3201.     * Replaces link indexes in the help text with topic_num, topic_off and
  3202.     * doc_page info.
  3203.     */
  3204.    {
  3205.    int         size;
  3206.    int         tok;
  3207.    LINK     *l;
  3208.  
  3209.    while (len > 0)
  3210.       {
  3211.       tok = find_token_length(0, curr, len, &size, NULL);
  3212.  
  3213.       if ( tok == TOK_LINK )
  3214.      {
  3215.          l = &a_link[ getint(curr+1) ];
  3216.          setint(curr+1,l->topic_num);
  3217.          setint(curr+1+sizeof(int),l->topic_off);
  3218.          setint(curr+1+2*sizeof(int),l->doc_page);
  3219.      }
  3220.  
  3221.       len -= size;
  3222.       curr += size;
  3223.       }
  3224.    }
  3225.  
  3226.  
  3227. void _write_help(FILE *file)
  3228.    {
  3229.    int             t, p, l, c;
  3230.    char             *text;
  3231.    TOPIC            *tp;
  3232.    CONTENT            *cp;
  3233.    struct help_sig_info  hs;
  3234.  
  3235.    /* write the signature and version */
  3236.  
  3237.    hs.sig = HELP_SIG; /* Edit line 17 of helpcom.h if this is a syntax error */
  3238.    hs.version = version;
  3239.  
  3240.    fwrite(&hs, sizeof(long)+sizeof(int), 1, file);
  3241.  
  3242.    /* write max_pages & max_links */
  3243.  
  3244.    putw(max_pages, file);
  3245.    putw(max_links, file);
  3246.  
  3247.    /* write num_topic, num_label and num_contents */
  3248.  
  3249.    putw(num_topic, file);
  3250.    putw(num_label, file);
  3251.    putw(num_contents, file);
  3252.  
  3253.    /* write num_doc_page */
  3254.  
  3255.    putw(num_doc_pages, file);
  3256.  
  3257.    /* write the offsets to each topic */
  3258.  
  3259.    for (t=0; t<num_topic; t++) 
  3260.       fwrite(&topic[t].offset, sizeof(long), 1, file);
  3261.  
  3262.    /* write all public labels */
  3263.  
  3264.    for (l=0; l<num_label; l++)
  3265.       {
  3266.       putw(label[l].topic_num, file);
  3267.       putw(label[l].topic_off, file);
  3268.       }
  3269.  
  3270.    /* write contents */
  3271.  
  3272.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3273.       {
  3274.       putw(cp->flags, file);
  3275.  
  3276.       t = strlen(cp->id);
  3277.       putc((BYTE)t, file);
  3278.       fwrite(cp->id, 1, t, file);
  3279.  
  3280.       t = strlen(cp->name);
  3281.       putc((BYTE)t, file);
  3282.       fwrite(cp->name, 1, t, file);
  3283.  
  3284.       putc((BYTE)cp->num_topic, file);
  3285.       fwrite(cp->topic_num, sizeof(int), cp->num_topic, file);
  3286.       }
  3287.  
  3288.    /* write topics */
  3289.  
  3290.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3291.       {
  3292.       /* write the topics flags */
  3293.  
  3294.       putw(tp->flags, file);
  3295.  
  3296.       /* write offset, length and starting margin for each page */
  3297.  
  3298.       putw(tp->num_page, file);
  3299.       for (p=0; p<tp->num_page; p++)
  3300.      {
  3301.      putw(tp->page[p].offset, file);
  3302.      putw(tp->page[p].length, file);
  3303.      putw(tp->page[p].margin, file);
  3304.      }
  3305.  
  3306.       /* write the help title */
  3307.  
  3308.       putc((BYTE)tp->title_len, file);
  3309.       fwrite(tp->title, 1, tp->title_len, file);
  3310.  
  3311.       /* insert hot-link info & write the help text */
  3312.  
  3313.       text = get_topic_text(tp);
  3314.  
  3315.       if ( !(tp->flags & TF_DATA) )   /* don't process data topics... */
  3316.      insert_real_link_info(text, tp->text_len);
  3317.  
  3318.       putw(tp->text_len, file);
  3319.       fwrite(text, 1, tp->text_len, file);
  3320.  
  3321.       release_topic_text(tp, 0);  /* don't save the text even though        */
  3322.                   /* insert_real_link_info() modified it    */
  3323.                   /* because we don't access the info after */
  3324.                   /* this.                    */
  3325.  
  3326.       }
  3327.    }
  3328.  
  3329.  
  3330. void write_help(char *fname)
  3331.    {
  3332.    FILE *hlp;
  3333.  
  3334.    hlp = fopen(fname, "wb");
  3335.  
  3336.    if (hlp == NULL)
  3337.       fatal(0,"Cannot create .HLP file: \"%s\".", fname);
  3338.  
  3339.    msg("Writing: %s", fname);
  3340.  
  3341.    _write_help(hlp);
  3342.  
  3343.    fclose(hlp);
  3344.    }
  3345.  
  3346.  
  3347. /*
  3348.  * print document stuff.
  3349.  */
  3350.  
  3351.  
  3352. typedef struct
  3353.    {
  3354.  
  3355.    /*
  3356.     * Note: Don't move these first three or pd_get_info will work not
  3357.     *        correctly.
  3358.     */
  3359.  
  3360.    int        cnum;
  3361.    int        tnum;
  3362.    int        link_dest_warn;   /* = 0 */
  3363.  
  3364.    FILE    *file;
  3365.    int        margin;
  3366.    int        start_of_line;
  3367.    int        spaces;
  3368.    } PRINT_DOC_INFO;
  3369.  
  3370.  
  3371. void printerc(PRINT_DOC_INFO *info, int c, int n)
  3372.    {
  3373.    while ( n-- > 0 )
  3374.       {
  3375.       if (c==' ')
  3376.      ++info->spaces;
  3377.  
  3378.       else if (c=='\n' || c=='\f')
  3379.      {
  3380.      info->start_of_line = 1;
  3381.      info->spaces = 0;   /* strip spaces before a new-line */
  3382.      putc(c, info->file);
  3383.      }
  3384.  
  3385.       else
  3386.      {
  3387.      if (info->start_of_line)
  3388.         {
  3389.         info->spaces += info->margin;
  3390.         info->start_of_line = 0;
  3391.         }
  3392.  
  3393.      while (info->spaces > 0)
  3394.         {
  3395.         fputc(' ', info->file);
  3396.         --info->spaces;
  3397.         }
  3398.  
  3399.      fputc(c, info->file);
  3400.      }
  3401.       }
  3402.    }
  3403.  
  3404.  
  3405. void printers(PRINT_DOC_INFO *info, char far *s, int n)
  3406.    {
  3407.    if (n > 0)
  3408.       {
  3409.       while ( n-- > 0 )
  3410.      printerc(info, *s++, 1);
  3411.       }
  3412.    else
  3413.       {
  3414.       while ( *s != '\0' )
  3415.      printerc(info, *s++, 1);
  3416.       }
  3417.    }
  3418.  
  3419.  
  3420. int print_doc_output(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
  3421.    {
  3422.    switch (cmd)
  3423.       {
  3424.       case PD_HEADING:
  3425.      {
  3426.      char buff[20];
  3427.  
  3428.      info->margin = 0;
  3429.      printers(info, "\n                     Fractint Version xx.xx                     Page ", 0);
  3430.      sprintf(buff, "%d\n\n", pd->pnum);
  3431.      printers(info, buff, 0);
  3432.      info->margin = PAGE_INDENT;
  3433.      return (1);
  3434.      }
  3435.  
  3436.       case PD_FOOTING:
  3437.      info->margin = 0;
  3438.      printerc(info, '\f', 1);
  3439.      info->margin = PAGE_INDENT;
  3440.      return (1);
  3441.  
  3442.       case PD_PRINT:
  3443.      printers(info, pd->s, pd->i);
  3444.      return (1);
  3445.  
  3446.       case PD_PRINTN:
  3447.      printerc(info, *pd->s, pd->i);
  3448.      return (1);
  3449.  
  3450.       case PD_PRINT_SEC:
  3451.      info->margin = TITLE_INDENT;
  3452.      if (pd->id[0] != '\0')
  3453.         {
  3454.         printers(info, pd->id, 0);
  3455.         printerc(info, ' ', 1);
  3456.         }
  3457.      printers(info, pd->title, 0);
  3458.      printerc(info, '\n', 1);
  3459.      info->margin = PAGE_INDENT;
  3460.      return (1);
  3461.  
  3462.       case PD_START_SECTION:
  3463.       case PD_START_TOPIC:
  3464.       case PD_SET_SECTION_PAGE:
  3465.       case PD_SET_TOPIC_PAGE:
  3466.       case PD_PERIODIC:
  3467.      return (1);
  3468.  
  3469.       default:
  3470.      return (0);
  3471.       }
  3472.    }
  3473.  
  3474.  
  3475. void print_document(char *fname)
  3476.    {
  3477.    PRINT_DOC_INFO info;
  3478.  
  3479.    if (num_contents == 0)
  3480.       fatal(0,".SRC has no DocContents.");
  3481.  
  3482.    msg("Printing to: %s", fname);
  3483.  
  3484.    info.cnum = info.tnum = -1;
  3485.    info.link_dest_warn = 0;
  3486.  
  3487.    if ( (info.file = fopen(fname, "wt")) == NULL )
  3488.       fatal(0,"Couldn't create \"%s\"", fname);
  3489.  
  3490.    info.margin = PAGE_INDENT;
  3491.    info.start_of_line = 1;
  3492.    info.spaces = 0;
  3493.  
  3494.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)print_doc_output, &info);
  3495.  
  3496.    fclose(info.file);
  3497.    }
  3498.  
  3499.  
  3500. /*
  3501.  * compiler status and memory usage report stuff.
  3502.  */
  3503.  
  3504.  
  3505. void report_memory(void)
  3506.    {
  3507.    long string = 0,   /* bytes in strings */
  3508.         text   = 0,   /* bytes in topic text (stored on disk) */
  3509.         data   = 0,   /* bytes in active data structure */
  3510.         dead   = 0;   /* bytes in unused data structure */
  3511.    int  ctr, ctr2;
  3512.  
  3513.    for (ctr=0; ctr<num_topic; ctr++)
  3514.       {
  3515.       data   += sizeof(TOPIC);
  3516.       string += topic[ctr].title_len;
  3517.       text   += topic[ctr].text_len;
  3518.       data   += topic[ctr].num_page * sizeof(PAGE);
  3519.  
  3520.       dead   += (PAGE_ALLOC_SIZE-(topic[ctr].num_page%PAGE_ALLOC_SIZE)) * sizeof(PAGE);
  3521.       }
  3522.  
  3523.    for (ctr=0; ctr<num_link; ctr++)
  3524.       {
  3525.       data += sizeof(LINK);
  3526.       string += strlen(a_link[ctr].name);
  3527.       }
  3528.  
  3529.    if (num_link > 0)
  3530.       dead += (LINK_ALLOC_SIZE-(num_link%LINK_ALLOC_SIZE)) * sizeof(LINK);
  3531.  
  3532.    for (ctr=0; ctr<num_label; ctr++)
  3533.       {
  3534.       data   += sizeof(LABEL);
  3535.       string += strlen(label[ctr].name) + 1;
  3536.       }
  3537.  
  3538.    if (num_label > 0)
  3539.       dead += (LABEL_ALLOC_SIZE-(num_label%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3540.  
  3541.    for (ctr=0; ctr<num_plabel; ctr++)
  3542.       {
  3543.       data   += sizeof(LABEL);
  3544.       string += strlen(plabel[ctr].name) + 1;
  3545.       }
  3546.  
  3547.    if (num_plabel > 0)
  3548.       dead += (LABEL_ALLOC_SIZE-(num_plabel%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3549.  
  3550.    for (ctr=0; ctr<num_contents; ctr++)
  3551.       {
  3552.       int t;
  3553.  
  3554.       t = ( MAX_CONTENT_TOPIC - contents[ctr].num_topic ) *
  3555.       ( sizeof(contents[0].is_label[0])   +
  3556.         sizeof(contents[0].topic_name[0]) +
  3557.         sizeof(contents[0].topic_num[0])     );
  3558.       data += sizeof(CONTENT) - t;
  3559.       dead += t;
  3560.       string += strlen(contents[ctr].id) + 1;
  3561.       string += strlen(contents[ctr].name) + 1;
  3562.       for (ctr2=0; ctr2<contents[ctr].num_topic; ctr2++)
  3563.      string += strlen(contents[ctr].topic_name[ctr2]) + 1;
  3564.       }
  3565.  
  3566.    dead += (CONTENTS_ALLOC_SIZE-(num_contents%CONTENTS_ALLOC_SIZE)) * sizeof(CONTENT);
  3567.  
  3568.    printf("\n");
  3569.    printf("Memory Usage:\n");
  3570.    printf("%8ld Bytes in buffers.\n", (long)BUFFER_SIZE);
  3571.    printf("%8ld Bytes in strings.\n", string);
  3572.    printf("%8ld Bytes in data.\n", data);
  3573.    printf("%8ld Bytes in dead space.\n", dead);
  3574.    printf("--------\n");
  3575.    printf("%8ld Bytes total.\n", (long)BUFFER_SIZE+string+data+dead);
  3576.    printf("\n");
  3577.    printf("Disk Usage:\n");
  3578.    printf("%8ld Bytes in topic text.\n", text);
  3579.    }
  3580.  
  3581.  
  3582. void report_stats(void)
  3583.    {
  3584.    int  pages = 0;
  3585.    int        t;
  3586.  
  3587.    for (t=0; t<num_topic; t++)
  3588.       pages += topic[t].num_page;
  3589.  
  3590.    printf("\n");
  3591.    printf("Statistics:\n");
  3592.    printf("%8d Topics\n", num_topic);
  3593.    printf("%8d Links\n", num_link);
  3594.    printf("%8d Labels\n", num_label);
  3595.    printf("%8d Private labels\n", num_plabel);
  3596.    printf("%8d Table of contents (DocContent) entries\n", num_contents);
  3597.    printf("%8d Online help pages\n", pages);
  3598.    printf("%8d Document pages\n", num_doc_pages);
  3599.    }
  3600.  
  3601.  
  3602. /*
  3603.  * add/delete help from .EXE functions.
  3604.  */
  3605.  
  3606.  
  3607. void add_hlp_to_exe(char *hlp_fname, char *exe_fname)
  3608.    {
  3609.    int                exe,   /* handles */
  3610.                 hlp;
  3611.    long             len,
  3612.                 count;
  3613.    int                size;
  3614.    struct help_sig_info hs;
  3615.  
  3616.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3617.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3618.  
  3619.    if ( (hlp=open(hlp_fname, O_RDONLY|O_BINARY)) == -1 )
  3620.       fatal(0,"Unable to open \"%s\"", hlp_fname);
  3621.  
  3622.    msg("Appending %s to %s", hlp_fname, exe_fname);
  3623.  
  3624.    /* first, check and see if any help is currently installed */
  3625.  
  3626.    lseek(exe, filelength(exe) - sizeof(struct help_sig_info), SEEK_SET);
  3627.  
  3628.    read(exe, (char *)&hs, 10);
  3629.  
  3630.    if ( hs.sig == HELP_SIG )
  3631.       warn(0,"Overwriting previous help. (Version=%d)", hs.version);
  3632.    else
  3633.       hs.base = filelength(exe);
  3634.  
  3635.    /* now, let's see if their help file is for real (and get the version) */
  3636.  
  3637.    read(hlp, (char *)&hs, sizeof(long)+sizeof(int));
  3638.  
  3639.    if (hs.sig != HELP_SIG )
  3640.       fatal(0,"Help signature not found in %s", hlp_fname);
  3641.  
  3642.    msg("Help file %s Version=%d", hlp_fname, hs.version);
  3643.  
  3644.    /* append the help stuff, overwriting old help (if any) */
  3645.  
  3646.    lseek(exe, hs.base, SEEK_SET);
  3647.  
  3648.    len = filelength(hlp) - sizeof(long) - sizeof(int); /* adjust for the file signature & version */
  3649.  
  3650.    for (count=0; count<len; )
  3651.       {
  3652.       size = (int) min((long)BUFFER_SIZE, len-count);
  3653.       read(hlp, buffer, size);
  3654.       write(exe, buffer, size);
  3655.       count += size;
  3656.       }
  3657.  
  3658.    /* add on the signature, version and offset */
  3659.  
  3660.    write(exe, (char *)&hs, 10);
  3661.  
  3662.    chsize(exe, lseek(exe,0L,SEEK_CUR));/* truncate if old help was longer */
  3663.  
  3664.    close(exe);
  3665.    close(hlp);
  3666.    }
  3667.  
  3668.  
  3669. void delete_hlp_from_exe(char *exe_fname)
  3670.    {
  3671.    int     exe;   /* file handle */
  3672.    struct help_sig_info hs;
  3673.  
  3674.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3675.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3676.  
  3677.    msg("Deleting help from %s", exe_fname);
  3678.  
  3679.    /* see if any help is currently installed */
  3680.  
  3681. #ifndef XFRACT
  3682.    lseek(exe, filelength(exe) - 10, SEEK_SET);
  3683.    read(exe, (char *)&hs, 10);
  3684. #else
  3685.    lseek(exe, filelength(exe) - 12, SEEK_SET);
  3686.    read(exe, (char *)&hs, 12);
  3687. #endif
  3688.  
  3689.    if ( hs.sig == HELP_SIG )
  3690.       {
  3691.       chsize(exe, hs.base);   /* truncate at the start of the help */
  3692.       close(exe);
  3693.       }
  3694.    else
  3695.       {
  3696.       close(exe);
  3697.       fatal(0,"No help found in %s", exe_fname);
  3698.       }
  3699.    }
  3700.  
  3701.  
  3702. /*
  3703.  * command-line parser, etc.
  3704.  */
  3705.  
  3706.  
  3707. #define MODE_COMPILE 1
  3708. #define MODE_PRINT   2
  3709. #define MODE_APPEND  3
  3710. #define MODE_DELETE  4
  3711.  
  3712.  
  3713. int main(int argc, char *argv[])
  3714.    {
  3715.    int      show_stats = 0,
  3716.       show_mem   = 0;
  3717.    int      mode         = 0;
  3718.  
  3719.    char **arg;
  3720.    char   fname1[81],
  3721.       fname2[81];
  3722.    char   swappath[81];
  3723.  
  3724.    fname1[0] = fname2[0] = swappath[0] = 0;
  3725.  
  3726.    printf("HC - FRACTINT Help Compiler.\n\n");
  3727.  
  3728.    buffer = malloc(BUFFER_SIZE);
  3729.  
  3730.    if (buffer == NULL)
  3731.       fatal(0,"Not enough memory to allocate buffer.");
  3732.  
  3733.    for (arg= &argv[1]; argc>1; argc--, arg++)
  3734.       {
  3735.       switch ( (*arg)[0] )
  3736.      {
  3737.      case '/':
  3738.      case '-':
  3739.         switch ( (*arg)[1] )
  3740.            {
  3741.            case 'c':
  3742.           if (mode == 0)
  3743.              mode = MODE_COMPILE;
  3744.           else
  3745.              fatal(0,"Cannot have /c with /a, /d or /p");
  3746.           break;
  3747.  
  3748.            case 'a':
  3749.           if (mode == 0)
  3750.              mode = MODE_APPEND;
  3751.           else
  3752.              fatal(0,"Cannot have /a with /c, /d or /p");
  3753.           break;
  3754.  
  3755.            case 'd':
  3756.           if (mode == 0)
  3757.              mode = MODE_DELETE;
  3758.           else
  3759.              fatal(0,"Cannot have /d with /c, /a or /p");
  3760.           break;
  3761.  
  3762.            case 'p':
  3763.           if (mode == 0)
  3764.              mode = MODE_PRINT;
  3765.           else
  3766.              fatal(0,"Cannot have /p with /c, /a or /d");
  3767.           break;
  3768.  
  3769.            case 'm':
  3770.           if (mode == MODE_COMPILE)
  3771.              show_mem = 1;
  3772.           else
  3773.              fatal(0,"/m switch allowed only when compiling (/c)");
  3774.           break;
  3775.  
  3776.            case 's':
  3777.           if (mode == MODE_COMPILE)
  3778.              show_stats = 1;
  3779.           else
  3780.              fatal(0,"/s switch allowed only when compiling (/c)");
  3781.           break;
  3782.  
  3783.            case 'r':
  3784.           if (mode == MODE_COMPILE || mode == MODE_PRINT)
  3785.              strcpy(swappath, (*arg)+2);
  3786.           else
  3787.              fatal(0,"/r switch allowed when compiling (/c) or printing (/p)");
  3788.           break;
  3789.  
  3790.            case 'q':
  3791.           quiet_mode = 1;
  3792.           break;
  3793.  
  3794.            default:
  3795.           fatal(0,"Bad command-line switch /%c", (*arg)[1]);
  3796.           break;
  3797.            }
  3798.         break;
  3799.  
  3800.      default:   /* assume it is a fname */
  3801.         if (fname1[0] == '\0')
  3802.            strcpy(fname1, *arg);
  3803.         else if (fname2[0] == '\0')
  3804.            strcpy(fname2, *arg);
  3805.         else
  3806.            fatal(0,"Unexpected command-line argument \"%s\"", *arg);
  3807.         break;
  3808.      } /* switch */
  3809.       } /* for */
  3810.  
  3811.    strupr(fname1);
  3812.    strupr(fname2);
  3813.    strupr(swappath);
  3814.  
  3815.    switch (mode)
  3816.       {
  3817.       case 0:
  3818.          printf( "To compile a .SRC file:\n");
  3819.          printf( "      HC /c [/s] [/m] [/r[path]] [src_file]\n");
  3820.          printf( "         /s       = report statistics.\n");
  3821.          printf( "         /m       = report memory usage.\n");
  3822.          printf( "         /r[path] = set swap file path.\n");
  3823.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3824.          printf( "To print a .SRC file:\n");
  3825.          printf( "      HC /p [/r[path]] [src_file] [out_file]\n");
  3826.          printf( "         /r[path] = set swap file path.\n");
  3827.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3828.          printf( "         out_file = Filename to print to. Default is \"%s\"\n",
  3829.          DEFAULT_DOC_FNAME);
  3830.          printf( "To append a .HLP file to an .EXE file:\n");
  3831.          printf( "      HC /a [hlp_file] [exe_file]\n");
  3832.          printf( "         hlp_file = .HLP file.  Default is \"%s\"\n", DEFAULT_HLP_FNAME);
  3833.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3834.          printf( "To delete help info from an .EXE file:\n");
  3835.          printf( "      HC /d [exe_file]\n");
  3836.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3837.          printf( "\n");
  3838.          printf( "Use \"/q\" for quiet mode. (No status messages.)\n");
  3839.      break;
  3840.  
  3841.       case MODE_COMPILE:
  3842.      if (fname2[0] != '\0')
  3843.         fatal(0,"Unexpected command-line argument \"%s\"", fname2);
  3844.  
  3845.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3846.  
  3847.      strcat(swappath, SWAP_FNAME);
  3848.  
  3849.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3850.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3851.      swappos = 0;
  3852.  
  3853.      read_src(src_fname);
  3854.  
  3855.      if (hdr_fname[0] == '\0')
  3856.         error(0,"No .H file defined.  (Use \"~HdrFile=\")");
  3857.      if (hlp_fname[0] == '\0')
  3858.         error(0,"No .HLP file defined.  (Use \"~HlpFile=\")");
  3859.      if (version == -1)
  3860.         warn(0,"No help version has been defined.  (Use \"~Version=\")");
  3861.  
  3862.      /* order of these is very important... */
  3863.  
  3864.      make_hot_links();  /* do even if errors since it may report */
  3865.                 /* more... */
  3866.  
  3867.      if ( !errors )     paginate_online();
  3868.      if ( !errors )     paginate_document();
  3869.      if ( !errors )     calc_offsets();
  3870.      if ( !errors )     sort_labels();
  3871.      if ( !errors )     write_hdr(hdr_fname);
  3872.      if ( !errors )     write_help(hlp_fname);
  3873.  
  3874.      if ( show_stats )
  3875.         report_stats();
  3876.  
  3877.      if ( show_mem )
  3878.         report_memory();
  3879.  
  3880.      if ( errors || warnings )
  3881.         report_errors();
  3882.  
  3883.      fclose(swapfile);
  3884.      remove(swappath);
  3885.  
  3886.      break;
  3887.  
  3888.       case MODE_PRINT:
  3889.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3890.  
  3891.      strcat(swappath, SWAP_FNAME);
  3892.  
  3893.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3894.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3895.      swappos = 0;
  3896.  
  3897.      read_src(src_fname);
  3898.  
  3899.      make_hot_links();
  3900.  
  3901.      if ( !errors )     paginate_document();
  3902.      if ( !errors )     print_document( (fname2[0]=='\0') ? DEFAULT_DOC_FNAME : fname2 );
  3903.  
  3904.      if ( errors || warnings )
  3905.         report_errors();
  3906.  
  3907.      fclose(swapfile);
  3908.      remove(swappath);
  3909.  
  3910.      break;
  3911.  
  3912.       case MODE_APPEND:
  3913.      add_hlp_to_exe( (fname1[0]=='\0') ? DEFAULT_HLP_FNAME : fname1,
  3914.              (fname2[0]=='\0') ? DEFAULT_EXE_FNAME : fname2);
  3915.      break;
  3916.  
  3917.       case MODE_DELETE:
  3918.      if (fname2[0] != '\0')
  3919.         fatal(0,"Unexpected argument \"%s\"", fname2);
  3920.      delete_hlp_from_exe((fname1[0]=='\0') ? DEFAULT_EXE_FNAME : fname1);
  3921.      break;
  3922.       }
  3923.  
  3924.    free(buffer);
  3925.  
  3926.    return ( errors );   /* return the number of errors */
  3927.    }
  3928.  
  3929.  
  3930.